Index: chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8e444b4c1052a05414d6b1f0a45629f785c87f4e |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java |
@@ -0,0 +1,1839 @@ |
+// 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.toolbar; |
+ |
+import android.animation.Animator; |
+import android.animation.AnimatorListenerAdapter; |
+import android.animation.AnimatorSet; |
+import android.animation.ObjectAnimator; |
+import android.animation.ValueAnimator; |
+import android.animation.ValueAnimator.AnimatorUpdateListener; |
+import android.annotation.SuppressLint; |
+import android.content.Context; |
+import android.content.res.ColorStateList; |
+import android.content.res.Resources; |
+import android.graphics.Canvas; |
+import android.graphics.Color; |
+import android.graphics.Point; |
+import android.graphics.PorterDuff; |
+import android.graphics.Rect; |
+import android.graphics.drawable.BitmapDrawable; |
+import android.graphics.drawable.ColorDrawable; |
+import android.graphics.drawable.Drawable; |
+import android.graphics.drawable.TransitionDrawable; |
+import android.os.Build; |
+import android.os.SystemClock; |
+import android.util.AttributeSet; |
+import android.util.Property; |
+import android.view.Gravity; |
+import android.view.MotionEvent; |
+import android.view.View; |
+import android.view.View.OnClickListener; |
+import android.view.View.OnLongClickListener; |
+import android.view.ViewGroup; |
+import android.view.WindowManager; |
+import android.view.animation.LinearInterpolator; |
+import android.widget.FrameLayout; |
+import android.widget.ImageView; |
+import android.widget.TextView; |
+import android.widget.Toast; |
+ |
+import com.google.android.apps.chrome.R; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.base.SysUtils; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.base.metrics.RecordUserAction; |
+import org.chromium.chrome.browser.Tab; |
+import org.chromium.chrome.browser.compositor.Invalidator; |
+import org.chromium.chrome.browser.document.BrandColorUtils; |
+import org.chromium.chrome.browser.ntp.NewTabPage; |
+import org.chromium.chrome.browser.omnibox.LocationBar; |
+import org.chromium.chrome.browser.omnibox.LocationBarPhone; |
+import org.chromium.chrome.browser.omnibox.UrlContainer; |
+import org.chromium.chrome.browser.partnercustomizations.HomepageManager; |
+import org.chromium.chrome.browser.util.FeatureUtilities; |
+import org.chromium.chrome.browser.util.MathUtils; |
+import org.chromium.chrome.browser.widget.TintedImageButton; |
+import org.chromium.chrome.browser.widget.newtab.NewTabButton; |
+import org.chromium.ui.base.LocalizationUtils; |
+import org.chromium.ui.interpolators.BakedBezierInterpolator; |
+ |
+import java.util.ArrayList; |
+import java.util.HashSet; |
+import java.util.List; |
+import java.util.Set; |
+ |
+/** |
+ * Phone specific toolbar implementation. |
+ */ |
+public class ToolbarPhone extends ToolbarLayout |
+ implements Invalidator.Client, OnClickListener, OnLongClickListener, |
+ NewTabPage.OnSearchBoxScrollListener { |
+ |
+ public static final int URL_FOCUS_CHANGE_ANIMATION_DURATION_MS = 250; |
+ private static final int URL_FOCUS_TOOLBAR_BUTTONS_TRANSLATION_X_DP = 10; |
+ private static final int URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS = 100; |
+ private static final int URL_CLEAR_FOCUS_TABSTACK_DELAY_MS = 200; |
+ private static final int URL_CLEAR_FOCUS_MENU_DELAY_MS = 250; |
+ |
+ private static final int TAB_SWITCHER_MODE_ENTER_ANIMATION_DURATION_MS = 200; |
+ private static final int TAB_SWITCHER_MODE_EXIT_NORMAL_ANIMATION_DURATION_MS = 200; |
+ private static final int TAB_SWITCHER_MODE_EXIT_FADE_ANIMATION_DURATION_MS = 100; |
+ private static final int TAB_SWITCHER_MODE_POST_EXIT_ANIMATION_DURATION_MS = 100; |
+ |
+ private static final float UNINITIALIZED_PERCENT = -1f; |
+ |
+ private static final int BRAND_COLOR_TRANSITION_DURATION_MS = 250; |
+ |
+ static final int LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA = 51; |
+ |
+ private LocationBarPhone mPhoneLocationBar; |
+ |
+ private View mToolbarButtonsContainer; |
+ private ImageView mToggleTabStackButton; |
+ private NewTabButton mNewTabButton; |
+ private TintedImageButton mHomeButton; |
+ private TextView mUrlBar; |
+ private UrlContainer mUrlContainer; |
+ private View mUrlActionsContainer; |
+ private ImageView mToolbarShadow; |
+ |
+ private ObjectAnimator mTabSwitcherModeAnimation; |
+ private ObjectAnimator mDelayedTabSwitcherModeAnimation; |
+ |
+ private final List<View> mTabSwitcherModeViews = new ArrayList<View>(); |
+ private final Set<View> mBrowsingModeViews = new HashSet<View>(); |
+ private boolean mInTabSwitcherMode; |
+ // This determines whether or not the toolbar draws as expected (false) or whether it always |
+ // draws as if it's showing the non-tabswitcher, non-animating toolbar. This is used in grabbing |
+ // a bitmap to use as a texture representation of this view. |
+ private boolean mTextureCaptureMode; |
+ private boolean mAnimateNormalToolbar; |
+ private boolean mDelayingTabSwitcherAnimation; |
+ |
+ private ColorDrawable mTabSwitcherAnimationBgOverlay; |
+ private TabSwitcherDrawable mTabSwitcherAnimationTabStackDrawable; |
+ private Drawable mTabSwitcherAnimationMenuDrawable; |
+ // Value that determines the amount of transition from the normal toolbar mode to TabSwitcher |
+ // mode. 0 = entirely in normal mode and 1.0 = entirely in TabSwitcher mode. In between values |
+ // can be used for animating between the two view modes. |
+ private float mTabSwitcherModePercent = 0; |
+ private boolean mUIAnimatingTabSwitcherTransition; |
+ |
+ // Used to clip the toolbar during the fade transition into and out of TabSwitcher mode. Only |
+ // used when |mAnimateNormalToolbar| is false. |
+ private Rect mClipRect; |
+ |
+ private OnClickListener mTabSwitcherListener; |
+ private OnClickListener mNewTabListener; |
+ |
+ private boolean mUrlFocusChangeInProgress; |
+ /** 1.0 is 100% focused, 0 is completely unfocused */ |
+ private float mUrlFocusChangePercent; |
+ /** 1.0 is 100% expanded to full width, 0 is original collapsed size. */ |
+ private float mUrlExpansionPercent; |
+ private AnimatorSet mUrlFocusLayoutAnimator; |
+ private boolean mDisableLocationBarRelayout; |
+ private boolean mLayoutLocationBarInFocusedMode; |
+ private int mUnfocusedLocationBarLayoutWidth; |
+ private int mUnfocusedLocationBarLayoutLeft; |
+ private boolean mUnfocusedLocationBarUsesTransparentBg; |
+ |
+ private int mUrlBackgroundAlpha = 255; |
+ private float mNtpSearchBoxScrollPercent = UNINITIALIZED_PERCENT; |
+ private ColorDrawable mToolbarBackground; |
+ private Drawable mLocationBarBackground; |
+ private boolean mForceDrawLocationBarBackground; |
+ private TabSwitcherDrawable mTabSwitcherButtonDrawable; |
+ private TabSwitcherDrawable mTabSwitcherButtonDrawableLight; |
+ |
+ private final Rect mUrlViewportBounds = new Rect(); |
+ private final Rect mUrlBackgroundPadding = new Rect(); |
+ private final Rect mBackgroundOverlayBounds = new Rect(); |
+ private final Rect mLocationBarBackgroundOffset = new Rect(); |
+ |
+ private final Rect mNtpSearchBoxOriginalBounds = new Rect(); |
+ private final Rect mNtpSearchBoxTransformedBounds = new Rect(); |
+ |
+ private final int mLocationBarInsets; |
+ private final int mToolbarSidePadding; |
+ |
+ private ValueAnimator mBrandColorTransitionAnimation; |
+ private boolean mBrandColorTransitionActive; |
+ |
+ /** |
+ * Used to specify the visual state of the toolbar. |
+ */ |
+ private enum VisualState { |
+ TAB_SWITCHER_INCOGNITO, |
+ TAB_SWITCHER_NORMAL, |
+ NORMAL, |
+ INCOGNITO, |
+ BRAND_COLOR, |
+ NEW_TAB_NORMAL |
+ } |
+ |
+ private VisualState mVisualState = VisualState.NORMAL; |
+ private VisualState mOverlayDrawablesVisualState; |
+ private boolean mUseLightToolbarDrawables; |
+ |
+ private NewTabPage mVisibleNewTabPage; |
+ private float mPreTextureCaptureAlpha = 1f; |
+ private boolean mIsOverlayTabStackDrawableLight; |
+ |
+ // The following are some properties used during animation. We use explicit property classes |
+ // to avoid the cost of reflection for each animation setup. |
+ |
+ private final Property<ToolbarPhone, Float> mUrlFocusChangePercentProperty = |
+ new Property<ToolbarPhone, Float>(Float.class, "") { |
+ @Override |
+ public Float get(ToolbarPhone object) { |
+ return object.mUrlFocusChangePercent; |
+ } |
+ |
+ @Override |
+ public void set(ToolbarPhone object, Float value) { |
+ setUrlFocusChangePercent(value); |
+ } |
+ }; |
+ |
+ private final Property<ToolbarPhone, Float> mTabSwitcherModePercentProperty = |
+ new Property<ToolbarPhone, Float>(Float.class, "") { |
+ @Override |
+ public Float get(ToolbarPhone object) { |
+ return object.mTabSwitcherModePercent; |
+ } |
+ |
+ @Override |
+ public void set(ToolbarPhone object, Float value) { |
+ object.mTabSwitcherModePercent = value; |
+ triggerPaintInvalidate(ToolbarPhone.this); |
+ } |
+ }; |
+ |
+ /** |
+ * Constructs a ToolbarPhone object. |
+ * @param context The Context in which this View object is created. |
+ * @param attrs The AttributeSet that was specified with this View. |
+ */ |
+ public ToolbarPhone(Context context, AttributeSet attrs) { |
+ super(context, attrs); |
+ mToolbarSidePadding = getResources().getDimensionPixelOffset( |
+ R.dimen.toolbar_edge_padding); |
+ // Insets used for the PhoneLocatioBar background drawable. |
+ mLocationBarInsets = getResources().getDimensionPixelSize(R.dimen.location_bar_margin_top) |
+ + getResources().getDimensionPixelSize(R.dimen.location_bar_margin_bottom); |
+ } |
+ |
+ @Override |
+ public void onFinishInflate() { |
+ super.onFinishInflate(); |
+ Context context = getContext(); |
+ |
+ mPhoneLocationBar = (LocationBarPhone) findViewById(R.id.location_bar); |
+ |
+ mToolbarButtonsContainer = findViewById(R.id.toolbar_buttons); |
+ |
+ mToggleTabStackButton = (ImageView) findViewById(R.id.tab_switcher_button); |
+ mToggleTabStackButton.setClickable(false); |
+ |
+ Resources resources = getResources(); |
+ mTabSwitcherButtonDrawable = |
+ TabSwitcherDrawable.createTabSwitcherDrawable(resources, false); |
+ mTabSwitcherButtonDrawableLight = |
+ TabSwitcherDrawable.createTabSwitcherDrawable(resources, true); |
+ |
+ mToggleTabStackButton.setVisibility(FeatureUtilities.isDocumentMode(getContext()) |
+ ? GONE : VISIBLE); |
+ mToggleTabStackButton.setImageDrawable(mTabSwitcherButtonDrawable); |
+ |
+ mNewTabButton = (NewTabButton) findViewById(R.id.new_tab_button); |
+ mHomeButton = (TintedImageButton) findViewById(R.id.home_button); |
+ |
+ mUrlBar = (TextView) findViewById(R.id.url_bar); |
+ mUrlContainer = (UrlContainer) findViewById(R.id.url_container); |
+ |
+ mUrlActionsContainer = findViewById(R.id.url_action_container); |
+ |
+ mTabSwitcherModeViews.add(mNewTabButton); |
+ mBrowsingModeViews.add(mPhoneLocationBar); |
+ |
+ mToolbarBackground = new ColorDrawable(getToolbarColorForVisualState(VisualState.NORMAL)); |
+ |
+ mTabSwitcherAnimationBgOverlay = |
+ new ColorDrawable(getToolbarColorForVisualState(VisualState.NORMAL)); |
+ |
+ mLocationBarBackground = |
+ ApiCompatibilityUtils.getDrawable(getResources(), R.drawable.inset_textbox); |
+ mLocationBarBackground.getPadding(mUrlBackgroundPadding); |
+ mPhoneLocationBar.setPadding( |
+ mUrlBackgroundPadding.left, mUrlBackgroundPadding.top, |
+ mUrlBackgroundPadding.right, mUrlBackgroundPadding.bottom); |
+ |
+ setLayoutTransition(null); |
+ |
+ mMenuButton.setVisibility(shouldShowMenuButton() ? View.VISIBLE : View.GONE); |
+ |
+ // Ensure that the new tab button will not draw over the toolbar buttons if the translated |
+ // string is long. Set a margin to the size of the toolbar button container for the new |
+ // tab button. |
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); |
+ Point screenSize = new Point(); |
+ wm.getDefaultDisplay().getSize(screenSize); |
+ |
+ mToolbarButtonsContainer.measure( |
+ MeasureSpec.makeMeasureSpec(screenSize.x, MeasureSpec.AT_MOST), |
+ MeasureSpec.makeMeasureSpec(screenSize.y, MeasureSpec.AT_MOST)); |
+ ApiCompatibilityUtils.setMarginEnd(getFrameLayoutParams(mNewTabButton), |
+ mToolbarButtonsContainer.getMeasuredWidth()); |
+ |
+ setWillNotDraw(false); |
+ } |
+ |
+ /** |
+ * Sets up click and key listeners once we have native library available to handle clicks. |
+ */ |
+ @Override |
+ public void onNativeLibraryReady() { |
+ super.onNativeLibraryReady(); |
+ getLocationBar().onNativeLibraryReady(); |
+ mToggleTabStackButton.setOnClickListener(this); |
+ mToggleTabStackButton.setOnLongClickListener(this); |
+ mToggleTabStackButton.setOnKeyListener(new KeyboardNavigationListener() { |
+ @Override |
+ public View getNextFocusForward() { |
+ if (mMenuButton != null && mMenuButton.isShown()) { |
+ return mMenuButton; |
+ } else { |
+ return getCurrentTabView(); |
+ } |
+ } |
+ |
+ @Override |
+ public View getNextFocusBackward() { |
+ return findViewById(R.id.url_bar); |
+ } |
+ }); |
+ mNewTabButton.setOnClickListener(this); |
+ mHomeButton.setOnClickListener(this); |
+ |
+ mMenuButton.setOnKeyListener(new KeyboardNavigationListener() { |
+ @Override |
+ public View getNextFocusForward() { |
+ return getCurrentTabView(); |
+ } |
+ |
+ @Override |
+ public View getNextFocusBackward() { |
+ return mToggleTabStackButton; |
+ } |
+ |
+ @Override |
+ protected boolean handleEnterKeyPress() { |
+ return getMenuButtonHelper().onEnterKeyPress(mMenuButton); |
+ } |
+ }); |
+ onHomeButtonUpdate(HomepageManager.isHomepageEnabled(getContext())); |
+ |
+ updateVisualsForToolbarState(mInTabSwitcherMode); |
+ } |
+ |
+ @Override |
+ public boolean onInterceptTouchEvent(MotionEvent ev) { |
+ // If the NTP is partially scrolled, prevent all touch events to the child views. This |
+ // is to not allow a secondary touch event to trigger entering the tab switcher, which |
+ // can lead to really odd snapshots and transitions to the switcher. |
+ if (mNtpSearchBoxScrollPercent != 0f |
+ && mNtpSearchBoxScrollPercent != 1f |
+ && mNtpSearchBoxScrollPercent != UNINITIALIZED_PERCENT) { |
+ return true; |
+ } |
+ return super.onInterceptTouchEvent(ev); |
+ } |
+ |
+ @Override |
+ public void onClick(View v) { |
+ if (mToggleTabStackButton == v) { |
+ // The button is clickable before the native library is loaded |
+ // and the listener is setup. |
+ if (mToggleTabStackButton != null && mToggleTabStackButton.isClickable() |
+ && mTabSwitcherListener != null) { |
+ mTabSwitcherListener.onClick(mToggleTabStackButton); |
+ RecordUserAction.record("MobileToolbarShowStackView"); |
+ } |
+ } else if (mNewTabButton == v) { |
+ v.setEnabled(false); |
+ |
+ if (mNewTabListener != null) { |
+ mNewTabListener.onClick(v); |
+ RecordUserAction.record("MobileToolbarStackViewNewTab"); |
+ RecordUserAction.record("MobileNewTabOpened"); |
+ // TODO(kkimlabs): Record UMA action for homepage button. |
+ } |
+ } else if (mHomeButton == v) { |
+ openHomepage(); |
+ } |
+ } |
+ |
+ @Override |
+ public boolean onLongClick(View v) { |
+ int stringResourceId = -1; |
+ if (v == mToggleTabStackButton) { |
+ stringResourceId = R.string.open_tabs; |
+ } |
+ if (stringResourceId != -1) { |
+ Context ctx = getContext(); |
+ // Display the tooltip for the view being long clicked. |
+ final int screenWidth = getResources().getDisplayMetrics().widthPixels; |
+ final int[] screenPos = new int[2]; |
+ v.getLocationOnScreen(screenPos); |
+ final int width = v.getWidth(); |
+ |
+ Toast toast = Toast.makeText( |
+ ctx, getResources().getString(stringResourceId), Toast.LENGTH_SHORT); |
+ toast.setGravity( |
+ Gravity.TOP | Gravity.END, |
+ screenWidth - screenPos[0] - width / 2, |
+ getHeight()); |
+ toast.show(); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ @Override |
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
+ if (!mDisableLocationBarRelayout) { |
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
+ |
+ boolean changed = layoutLocationBar(MeasureSpec.getSize(widthMeasureSpec)); |
+ setUrlFocusChangePercent(mUrlFocusChangePercent); |
+ if (!changed) return; |
+ } else { |
+ updateUnfocusedLocationBarLayoutParams(); |
+ } |
+ |
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
+ } |
+ |
+ private void updateUnfocusedLocationBarLayoutParams() { |
+ boolean hasVisibleViewPriorToUrlBar = false; |
+ for (int i = 0; i < mPhoneLocationBar.getChildCount(); i++) { |
+ View child = mPhoneLocationBar.getChildAt(i); |
+ if (child == mUrlContainer) break; |
+ if (child.getVisibility() != GONE) { |
+ hasVisibleViewPriorToUrlBar = true; |
+ break; |
+ } |
+ } |
+ |
+ int leftViewBounds = getViewBoundsLeftOfLocationBar(); |
+ if (!hasVisibleViewPriorToUrlBar) leftViewBounds += mToolbarSidePadding; |
+ int rightViewBounds = getViewBoundsRightOfLocationBar(); |
+ |
+ if (!mPhoneLocationBar.hasVisibleViewsAfterUrlBarWhenUnfocused()) { |
+ // Add spacing between the end of the URL and the edge of the omnibox drawable. |
+ // This only applies if there is no end aligned view that should be visible |
+ // while the omnibox is unfocused. |
+ if (ApiCompatibilityUtils.isLayoutRtl(mPhoneLocationBar)) { |
+ leftViewBounds += mToolbarSidePadding; |
+ } else { |
+ rightViewBounds -= mToolbarSidePadding; |
+ } |
+ } |
+ |
+ mUnfocusedLocationBarLayoutWidth = rightViewBounds - leftViewBounds; |
+ mUnfocusedLocationBarLayoutLeft = leftViewBounds; |
+ } |
+ |
+ /** |
+ * @return The background drawable for the fullscreen overlay. |
+ */ |
+ @VisibleForTesting |
+ ColorDrawable getOverlayDrawable() { |
+ return mTabSwitcherAnimationBgOverlay; |
+ } |
+ |
+ /** |
+ * @return The background drawable for the toolbar view. |
+ */ |
+ @VisibleForTesting |
+ ColorDrawable getBackgroundDrawable() { |
+ return mToolbarBackground; |
+ } |
+ |
+ @SuppressLint("RtlHardcoded") |
+ private boolean layoutLocationBar(int containerWidth) { |
+ // Note that Toolbar's direction depends on system layout direction while |
+ // LocationBar's direction depends on its text inside. |
+ FrameLayout.LayoutParams locationBarLayoutParams = |
+ getFrameLayoutParams(getLocationBar().getContainerView()); |
+ |
+ // Chrome prevents layout_gravity="left" from being defined in XML, but it simplifies |
+ // the logic, so it is manually specified here. |
+ locationBarLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; |
+ |
+ int width = 0; |
+ int leftMargin = 0; |
+ |
+ // Always update the unfocused layout params regardless of whether we are using |
+ // those in this current layout pass as they are needed for animations. |
+ updateUnfocusedLocationBarLayoutParams(); |
+ |
+ if (mLayoutLocationBarInFocusedMode || mVisualState == VisualState.NEW_TAB_NORMAL) { |
+ int priorVisibleWidth = 0; |
+ for (int i = 0; i < mPhoneLocationBar.getChildCount(); i++) { |
+ View child = mPhoneLocationBar.getChildAt(i); |
+ if (child == mPhoneLocationBar.getFirstViewVisibleWhenFocused()) break; |
+ if (child.getVisibility() == GONE) continue; |
+ priorVisibleWidth += child.getMeasuredWidth(); |
+ } |
+ |
+ width = containerWidth - (2 * mToolbarSidePadding) + priorVisibleWidth; |
+ if (ApiCompatibilityUtils.isLayoutRtl(mPhoneLocationBar)) { |
+ leftMargin = mToolbarSidePadding; |
+ } else { |
+ leftMargin = -priorVisibleWidth + mToolbarSidePadding; |
+ } |
+ } else { |
+ width = mUnfocusedLocationBarLayoutWidth; |
+ leftMargin = mUnfocusedLocationBarLayoutLeft; |
+ } |
+ |
+ boolean changed = false; |
+ changed |= (width != locationBarLayoutParams.width); |
+ locationBarLayoutParams.width = width; |
+ |
+ changed |= (leftMargin != locationBarLayoutParams.leftMargin); |
+ locationBarLayoutParams.leftMargin = leftMargin; |
+ |
+ return changed; |
+ } |
+ |
+ private int getViewBoundsLeftOfLocationBar() { |
+ // Uses getMeasuredWidth()s instead of getLeft() because this is called in onMeasure |
+ // and the layout values have not yet been set. |
+ if (mVisualState == VisualState.NEW_TAB_NORMAL) { |
+ return 0; |
+ } else if (ApiCompatibilityUtils.isLayoutRtl(this)) { |
+ return Math.max( |
+ mToolbarSidePadding, mToolbarButtonsContainer.getMeasuredWidth()); |
+ } else { |
+ return mHomeButton.getVisibility() != GONE |
+ ? mHomeButton.getMeasuredWidth() : mToolbarSidePadding; |
+ } |
+ } |
+ |
+ private int getViewBoundsRightOfLocationBar() { |
+ // Uses getMeasuredWidth()s instead of getRight() because this is called in onMeasure |
+ // and the layout values have not yet been set. |
+ if (mVisualState == VisualState.NEW_TAB_NORMAL) { |
+ return getMeasuredWidth(); |
+ } else if (ApiCompatibilityUtils.isLayoutRtl(this)) { |
+ return getMeasuredWidth() - (mHomeButton.getVisibility() != GONE |
+ ? mHomeButton.getMeasuredWidth() : mToolbarSidePadding); |
+ } else { |
+ int margin = Math.max( |
+ mToolbarSidePadding, mToolbarButtonsContainer.getMeasuredWidth()); |
+ return getMeasuredWidth() - margin; |
+ } |
+ } |
+ |
+ private void updateToolbarBackground(int color) { |
+ mToolbarBackground.setColor(color); |
+ invalidate(); |
+ } |
+ |
+ private void updateToolbarBackground(VisualState visualState) { |
+ updateToolbarBackground(getToolbarColorForVisualState(visualState)); |
+ } |
+ |
+ private int getToolbarColorForVisualState(final VisualState visualState) { |
+ Resources res = getResources(); |
+ switch (visualState) { |
+ case NEW_TAB_NORMAL: |
+ return Color.TRANSPARENT; |
+ case NORMAL : |
+ return res.getColor(R.color.default_primary_color); |
+ case INCOGNITO: |
+ return res.getColor(R.color.incognito_primary_color); |
+ case BRAND_COLOR: |
+ return getToolbarDataProvider().getPrimaryColor(); |
+ case TAB_SWITCHER_NORMAL: |
+ case TAB_SWITCHER_INCOGNITO: |
+ return res.getColor(R.color.tab_switcher_background); |
+ default: |
+ assert false; |
+ return res.getColor(R.color.default_primary_color); |
+ } |
+ } |
+ |
+ @Override |
+ protected void dispatchDraw(Canvas canvas) { |
+ if (!mTextureCaptureMode && mToolbarBackground.getColor() != Color.TRANSPARENT) { |
+ // Update to compensate for orientation changes. |
+ mToolbarBackground.setBounds(0, 0, getWidth(), getHeight()); |
+ mToolbarBackground.draw(canvas); |
+ } |
+ |
+ if (mLocationBarBackground != null |
+ && (mPhoneLocationBar.getVisibility() == VISIBLE || mTextureCaptureMode)) { |
+ // Calculate the visible boundaries of the left and right most child views |
+ // of the location bar. |
+ int leftViewPosition = getViewBoundsLeftOfLocationBar(); |
+ int rightViewPosition = getViewBoundsRightOfLocationBar(); |
+ |
+ leftViewPosition -= mUrlBackgroundPadding.left; |
+ if (mUrlExpansionPercent != 0f) { |
+ leftViewPosition *= (1f - mUrlExpansionPercent); |
+ leftViewPosition -= mUrlBackgroundPadding.left * mUrlExpansionPercent; |
+ } |
+ |
+ rightViewPosition += mUrlBackgroundPadding.right; |
+ if (mUrlExpansionPercent != 0f) { |
+ rightViewPosition += ((getWidth() - rightViewPosition) * mUrlExpansionPercent); |
+ rightViewPosition += mUrlBackgroundPadding.right * mUrlExpansionPercent; |
+ } |
+ |
+ // The bounds are set by the following: |
+ // - The left most visible location bar child view. |
+ // - The top of the viewport is aligned with the top of the location bar. |
+ // - The right most visible location bar child view. |
+ // - The bottom of the viewport is aligned with the bottom of the location bar. |
+ // Additional padding can be applied for use during animations. |
+ mUrlViewportBounds.set( |
+ leftViewPosition, |
+ 0, |
+ rightViewPosition, |
+ (int) (mPhoneLocationBar.getMeasuredHeight() |
+ + (getHeight() - mPhoneLocationBar.getMeasuredHeight() |
+ + mUrlBackgroundPadding.bottom + mUrlBackgroundPadding.top) |
+ * mUrlExpansionPercent)); |
+ mUrlViewportBounds.offset(0, (int) (mPhoneLocationBar.getY() |
+ - (mUrlBackgroundPadding.top * mUrlExpansionPercent))); |
+ } |
+ |
+ if (mTextureCaptureMode) { |
+ drawTabSwitcherAnimationOverlay(canvas, 0.f); |
+ } else { |
+ boolean tabSwitcherAnimationFinished = false; |
+ if (mTabSwitcherModeAnimation != null) { |
+ tabSwitcherAnimationFinished = !mTabSwitcherModeAnimation.isRunning(); |
+ |
+ // Perform the fade logic before super.dispatchDraw(canvas) so that we can properly |
+ // set the values before the draw happens. |
+ if (!mAnimateNormalToolbar) { |
+ drawTabSwitcherFadeAnimation( |
+ tabSwitcherAnimationFinished, mTabSwitcherModePercent); |
+ } |
+ } |
+ |
+ super.dispatchDraw(canvas); |
+ |
+ if (mTabSwitcherModeAnimation != null) { |
+ // Perform the overlay logic after super.dispatchDraw(canvas) as we need to draw on |
+ // top of the current views. |
+ if (mAnimateNormalToolbar) { |
+ drawTabSwitcherAnimationOverlay(canvas, mTabSwitcherModePercent); |
+ } |
+ |
+ // Clear the animation. |
+ if (tabSwitcherAnimationFinished) mTabSwitcherModeAnimation = null; |
+ } |
+ } |
+ } |
+ |
+ // NewTabPage.OnSearchBoxScrollListener |
+ @Override |
+ public void onScrollChanged(float scrollPercentage) { |
+ if (scrollPercentage == mNtpSearchBoxScrollPercent) return; |
+ |
+ mNtpSearchBoxScrollPercent = scrollPercentage; |
+ updateUrlExpansionPercent(); |
+ updateUrlExpansionAnimation(); |
+ } |
+ |
+ /** |
+ * Updates percentage of current the URL focus change animation. |
+ * @param percent 1.0 is 100% focused, 0 is completely unfocused. |
+ */ |
+ private void setUrlFocusChangePercent(float percent) { |
+ mUrlFocusChangePercent = percent; |
+ updateUrlExpansionPercent(); |
+ updateUrlExpansionAnimation(); |
+ } |
+ |
+ private void updateUrlExpansionPercent() { |
+ mUrlExpansionPercent = Math.max(mNtpSearchBoxScrollPercent, mUrlFocusChangePercent); |
+ assert mUrlExpansionPercent >= 0; |
+ assert mUrlExpansionPercent <= 1; |
+ } |
+ |
+ private void updateUrlExpansionAnimation() { |
+ mLocationBarBackgroundOffset.setEmpty(); |
+ |
+ FrameLayout.LayoutParams locationBarLayoutParams = |
+ getFrameLayoutParams(mPhoneLocationBar); |
+ int currentLeftMargin = locationBarLayoutParams.leftMargin; |
+ int currentWidth = locationBarLayoutParams.width; |
+ |
+ float inversePercent = 1f - mUrlExpansionPercent; |
+ boolean isLocationBarRtl = ApiCompatibilityUtils.isLayoutRtl(mPhoneLocationBar); |
+ if (ApiCompatibilityUtils.isLayoutRtl(mPhoneLocationBar)) { |
+ mPhoneLocationBar.setTranslationX( |
+ ((mUnfocusedLocationBarLayoutLeft + mUnfocusedLocationBarLayoutWidth) |
+ - (currentLeftMargin + currentWidth)) * inversePercent); |
+ } else { |
+ mPhoneLocationBar.setTranslationX( |
+ (mUnfocusedLocationBarLayoutLeft - currentLeftMargin) * inversePercent); |
+ mUrlActionsContainer.setTranslationX(-mPhoneLocationBar.getTranslationX()); |
+ } |
+ |
+ // Negate the location bar translation to keep the URL action container in the same |
+ // place during the focus expansion. The check for RTL parity is required because |
+ // if they do not match then the action container will overlap the URL if we do not |
+ // allow it to be pushed off. |
+ if (isLocationBarRtl == ApiCompatibilityUtils.isLayoutRtl(this)) { |
+ mUrlActionsContainer.setTranslationX(-mPhoneLocationBar.getTranslationX()); |
+ } |
+ |
+ mPhoneLocationBar.setUrlFocusChangePercent(mUrlExpansionPercent); |
+ |
+ // Ensure the buttons are invisible after focusing the omnibox to prevent them from |
+ // accepting click events. |
+ int toolbarButtonVisibility = mUrlExpansionPercent == 1f ? INVISIBLE : VISIBLE; |
+ mToolbarButtonsContainer.setVisibility(toolbarButtonVisibility); |
+ if (mHomeButton.getVisibility() != GONE) { |
+ mHomeButton.setVisibility(toolbarButtonVisibility); |
+ } |
+ |
+ // Force an invalidation of the location bar to properly handle the clipping of the URL |
+ // bar text as a result of the url action container translations. |
+ mPhoneLocationBar.invalidate(); |
+ invalidate(); |
+ |
+ Tab currentTab = getToolbarDataProvider().getTab(); |
+ if (currentTab == null) return; |
+ |
+ NewTabPage ntp = getToolbarDataProvider().getNewTabPageForCurrentTab(); |
+ // Explicitly use the focus change percentage here because it applies scroll compensation |
+ // that only applies during focus animations. |
+ if (ntp != null) ntp.setUrlFocusChangeAnimationPercent(mUrlFocusChangePercent); |
+ |
+ if (!isLocationBarShownInNTP()) { |
+ // Reset these values in case we transitioned to a different page during the |
+ // transition. |
+ resetNtpAnimationValues(); |
+ return; |
+ } |
+ |
+ updateNtpTransitionAnimation(ntp); |
+ } |
+ |
+ private void resetNtpAnimationValues() { |
+ mLocationBarBackgroundOffset.setEmpty(); |
+ mPhoneLocationBar.setTranslationY(0); |
+ if (!mUrlFocusChangeInProgress) { |
+ mToolbarButtonsContainer.setTranslationY(0); |
+ mHomeButton.setTranslationY(0); |
+ } |
+ mToolbarShadow.setAlpha(1f); |
+ mPhoneLocationBar.setAlpha(1); |
+ mForceDrawLocationBarBackground = false; |
+ mUrlBackgroundAlpha = isIncognito() |
+ || (mUnfocusedLocationBarUsesTransparentBg |
+ && !mUrlFocusChangeInProgress |
+ && !mPhoneLocationBar.hasFocus()) |
+ ? LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA : 255; |
+ setAncestorsShouldClipChildren(true); |
+ mNtpSearchBoxScrollPercent = UNINITIALIZED_PERCENT; |
+ } |
+ |
+ private void updateNtpTransitionAnimation(NewTabPage ntp) { |
+ if (mInTabSwitcherMode) return; |
+ |
+ setAncestorsShouldClipChildren(mUrlExpansionPercent == 0f); |
+ mToolbarShadow.setAlpha(0f); |
+ |
+ float growthPercent = 0f; |
+ if (mUrlExpansionPercent == 0f || mUrlExpansionPercent == 1f) { |
+ growthPercent = 1f - mUrlExpansionPercent; |
+ } else { |
+ // During the transition from search box to omnibox, keep the omnibox drawing |
+ // at the same size of the search box for first 40% of the scroll transition. |
+ growthPercent = mUrlExpansionPercent <= 0.4f |
+ ? 1f : Math.min(1f, (1f - mUrlExpansionPercent) * 1.66667f); |
+ } |
+ |
+ int paddingTop = mPhoneLocationBar.getPaddingTop(); |
+ int paddingBottom = mPhoneLocationBar.getPaddingBottom(); |
+ |
+ ntp.getSearchBoxBounds(mNtpSearchBoxOriginalBounds, mNtpSearchBoxTransformedBounds); |
+ float halfHeightDifference = (mNtpSearchBoxTransformedBounds.height() |
+ - (mPhoneLocationBar.getMeasuredHeight() - paddingTop - paddingBottom |
+ + mLocationBarInsets)) / 2f; |
+ mPhoneLocationBar.setTranslationY(growthPercent == 0f ? 0 : Math.max(0, |
+ (mNtpSearchBoxTransformedBounds.top - mPhoneLocationBar.getTop() |
+ + halfHeightDifference))); |
+ if (!mUrlFocusChangeInProgress) { |
+ float searchBoxTranslationY = |
+ mNtpSearchBoxTransformedBounds.top - mNtpSearchBoxOriginalBounds.top; |
+ mToolbarButtonsContainer.setTranslationY(searchBoxTranslationY); |
+ mHomeButton.setTranslationY(searchBoxTranslationY); |
+ } |
+ |
+ mLocationBarBackgroundOffset.set( |
+ (int) ((mNtpSearchBoxTransformedBounds.left - mUrlViewportBounds.left |
+ - mPhoneLocationBar.getPaddingLeft()) * growthPercent), |
+ (int) ((-halfHeightDifference - paddingTop) * growthPercent), |
+ (int) ((mNtpSearchBoxTransformedBounds.right - mUrlViewportBounds.right |
+ + mPhoneLocationBar.getPaddingRight()) * growthPercent), |
+ (int) ((halfHeightDifference - paddingBottom + mLocationBarInsets) |
+ * growthPercent)); |
+ |
+ // The transparency of the location bar is dependent on how different its size is |
+ // from the final value. This is based on how much growth is applied between the |
+ // desired size of the location bar to it's drawn size. The location bar then only |
+ // starts becoming opaque once the growth is at least half done. |
+ if (growthPercent >= 0.5f) { |
+ mPhoneLocationBar.setAlpha(0); |
+ } else { |
+ mPhoneLocationBar.setAlpha(1f - growthPercent * 2); |
+ } |
+ |
+ // Go from a transparent url background to a fully opaque one in the first 40% of the |
+ // scroll transition. |
+ mUrlBackgroundAlpha = |
+ mUrlExpansionPercent >= 0.4f ? 255 : (int) ((mUrlExpansionPercent * 2.5f) * 255); |
+ if (mUrlExpansionPercent == 1f) mUrlBackgroundAlpha = 255; |
+ mForceDrawLocationBarBackground = mUrlExpansionPercent != 0f; |
+ } |
+ |
+ private void setAncestorsShouldClipChildren(boolean clip) { |
+ if (!isLocationBarShownInNTP()) return; |
+ ViewGroup parent = this; |
+ while (parent != null) { |
+ parent.setClipChildren(clip); |
+ if (!(parent.getParent() instanceof ViewGroup)) break; |
+ if (parent.getId() == android.R.id.content) break; |
+ parent = (ViewGroup) parent.getParent(); |
+ } |
+ } |
+ |
+ private void drawTabSwitcherFadeAnimation(boolean animationFinished, float progress) { |
+ setAlpha(progress); |
+ if (animationFinished) { |
+ mClipRect = null; |
+ } else if (mClipRect == null) { |
+ mClipRect = new Rect(); |
+ } |
+ if (mClipRect != null) mClipRect.set(0, 0, getWidth(), (int) (getHeight() * progress)); |
+ } |
+ |
+ /** |
+ * When entering and exiting the TabSwitcher mode, we fade out or fade in the browsing |
+ * mode of the toolbar on top of the TabSwitcher mode version of it. We do this by |
+ * drawing all of the browsing mode views on top of the android view. |
+ */ |
+ private void drawTabSwitcherAnimationOverlay(Canvas canvas, float animationProgress) { |
+ if (!isNativeLibraryReady()) return; |
+ |
+ float floatAlpha = 1 - animationProgress; |
+ int rgbAlpha = (int) (255 * floatAlpha); |
+ canvas.save(); |
+ canvas.translate(0, -animationProgress * mBackgroundOverlayBounds.height()); |
+ canvas.clipRect(mBackgroundOverlayBounds); |
+ |
+ // Draw the background of the view we are leaving. |
+ mTabSwitcherAnimationBgOverlay.setBounds( |
+ 0, 0, getMeasuredWidth(), getMeasuredHeight()); |
+ if (isLocationBarShownInNTP()) { |
+ float ntpTransitionPercentage = mUrlExpansionPercent; |
+ boolean shouldDrawWhite = ntpTransitionPercentage != 1.0f; |
+ mTabSwitcherAnimationBgOverlay.setColor(shouldDrawWhite |
+ ? Color.WHITE : getToolbarColorForVisualState(VisualState.NORMAL)); |
+ } |
+ mTabSwitcherAnimationBgOverlay.draw(canvas); |
+ |
+ float previousAlpha = 0.f; |
+ if (mHomeButton.getVisibility() != View.GONE) { |
+ // Draw the New Tab button used in the URL view. |
+ previousAlpha = mHomeButton.getAlpha(); |
+ mHomeButton.setAlpha(previousAlpha * floatAlpha); |
+ drawChild(canvas, mHomeButton, SystemClock.uptimeMillis()); |
+ mHomeButton.setAlpha(previousAlpha); |
+ } |
+ |
+ // Draw the location/URL bar. |
+ previousAlpha = mPhoneLocationBar.getAlpha(); |
+ mPhoneLocationBar.setAlpha(previousAlpha * floatAlpha); |
+ // If the location bar is now fully transparent, do not bother drawing it. |
+ if (mPhoneLocationBar.getAlpha() != 0) { |
+ drawChild(canvas, mPhoneLocationBar, SystemClock.uptimeMillis()); |
+ } |
+ mPhoneLocationBar.setAlpha(previousAlpha); |
+ |
+ // Draw the tab stack button and associated text. |
+ translateCanvasToView(this, mToolbarButtonsContainer, canvas); |
+ |
+ if (mTabSwitcherAnimationTabStackDrawable != null |
+ && mToggleTabStackButton.getVisibility() != View.GONE) { |
+ // Draw the tab stack button image. |
+ canvas.save(); |
+ translateCanvasToView(mToolbarButtonsContainer, mToggleTabStackButton, canvas); |
+ |
+ int backgroundWidth = mToggleTabStackButton.getDrawable().getIntrinsicWidth(); |
+ int backgroundHeight = mToggleTabStackButton.getDrawable().getIntrinsicHeight(); |
+ int backgroundLeft = (mToggleTabStackButton.getWidth() |
+ - mToggleTabStackButton.getPaddingLeft() |
+ - mToggleTabStackButton.getPaddingRight() - backgroundWidth) / 2; |
+ backgroundLeft += mToggleTabStackButton.getPaddingLeft(); |
+ int backgroundTop = (mToggleTabStackButton.getHeight() |
+ - mToggleTabStackButton.getPaddingTop() |
+ - mToggleTabStackButton.getPaddingBottom() - backgroundHeight) / 2; |
+ backgroundTop += mToggleTabStackButton.getPaddingTop(); |
+ canvas.translate(backgroundLeft, backgroundTop); |
+ |
+ mTabSwitcherAnimationTabStackDrawable.setAlpha(rgbAlpha); |
+ mTabSwitcherAnimationTabStackDrawable.draw(canvas); |
+ canvas.restore(); |
+ } |
+ |
+ // Draw the menu button if necessary. |
+ if (mTabSwitcherAnimationMenuDrawable != null) { |
+ mTabSwitcherAnimationMenuDrawable.setBounds( |
+ mMenuButton.getPaddingLeft(), mMenuButton.getPaddingTop(), |
+ mMenuButton.getWidth() - mMenuButton.getPaddingRight(), |
+ mMenuButton.getHeight() - mMenuButton.getPaddingBottom()); |
+ translateCanvasToView(mToolbarButtonsContainer, mMenuButton, canvas); |
+ mTabSwitcherAnimationMenuDrawable.setAlpha(rgbAlpha); |
+ mTabSwitcherAnimationMenuDrawable.draw(canvas); |
+ } |
+ |
+ canvas.restore(); |
+ } |
+ |
+ @Override |
+ public void doInvalidate() { |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ /** |
+ * Translates the canvas to ensure the specified view's coordinates are at 0, 0. |
+ * |
+ * @param from The view the canvas is currently translated to. |
+ * @param to The view to translate to. |
+ * @param canvas The canvas to be translated. |
+ * |
+ * @throws IllegalArgumentException if {@code from} is not an ancestor of {@code to}. |
+ */ |
+ private static void translateCanvasToView(View from, View to, Canvas canvas) |
+ throws IllegalArgumentException { |
+ assert from != null; |
+ assert to != null; |
+ while (to != from) { |
+ canvas.translate(to.getLeft(), to.getTop()); |
+ if (!(to.getParent() instanceof View)) { |
+ throw new IllegalArgumentException("View 'to' was not a desendent of 'from'."); |
+ } |
+ to = (View) to.getParent(); |
+ } |
+ } |
+ |
+ @Override |
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { |
+ if (child == mPhoneLocationBar) return drawLocationBar(canvas, drawingTime); |
+ boolean clipped = false; |
+ |
+ if (mLocationBarBackground != null |
+ && ((!mInTabSwitcherMode && !mTabSwitcherModeViews.contains(child)) |
+ || (mInTabSwitcherMode && mBrowsingModeViews.contains(child)))) { |
+ canvas.save(); |
+ if (mUrlExpansionPercent != 0f && mUrlViewportBounds.top < child.getBottom()) { |
+ // For other child views, use the inverse clipping of the URL viewport. |
+ // Only necessary during animations. |
+ // Hardware mode does not support unioned clip regions, so clip using the |
+ // appropriate bounds based on whether the child is to the left or right of the |
+ // location bar. |
+ boolean isLeft = (child == mNewTabButton || child == mHomeButton) |
+ ^ LocalizationUtils.isLayoutRtl(); |
+ |
+ int clipTop = mUrlViewportBounds.top; |
+ int clipBottom = mUrlViewportBounds.bottom; |
+ boolean verticalClip = false; |
+ if (mPhoneLocationBar.getTranslationY() > 0f) { |
+ clipTop = child.getTop(); |
+ clipBottom = mUrlViewportBounds.top; |
+ verticalClip = true; |
+ } |
+ |
+ if (isLeft) { |
+ canvas.clipRect( |
+ 0, clipTop, |
+ verticalClip ? child.getMeasuredWidth() : mUrlViewportBounds.left, |
+ clipBottom); |
+ } else { |
+ canvas.clipRect( |
+ verticalClip ? 0 : mUrlViewportBounds.right, |
+ clipTop, getMeasuredWidth(), clipBottom); |
+ } |
+ } |
+ clipped = true; |
+ } |
+ boolean retVal = super.drawChild(canvas, child, drawingTime); |
+ if (clipped) canvas.restore(); |
+ return retVal; |
+ } |
+ |
+ private boolean drawLocationBar(Canvas canvas, long drawingTime) { |
+ boolean clipped = false; |
+ float locationBarClipLeft = 0; |
+ float locationBarClipRight = 0; |
+ float locationBarClipTop = 0; |
+ float locationBarClipBottom = 0; |
+ |
+ if (mLocationBarBackground != null && (!mInTabSwitcherMode || mTextureCaptureMode)) { |
+ canvas.save(); |
+ int backgroundAlpha = mUrlBackgroundAlpha; |
+ if (mTabSwitcherModeAnimation != null) { |
+ // Fade out/in the location bar towards the beginning of the animations to avoid |
+ // large jumps of stark white. |
+ backgroundAlpha = |
+ (int) (Math.pow(mPhoneLocationBar.getAlpha(), 3) * backgroundAlpha); |
+ } else if (getToolbarDataProvider().isUsingBrandColor() |
+ && !mBrandColorTransitionActive) { |
+ int unfocusedAlpha = mUnfocusedLocationBarUsesTransparentBg |
+ ? LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA : 255; |
+ backgroundAlpha = |
+ (int) (mUrlExpansionPercent * (255 - unfocusedAlpha) + unfocusedAlpha); |
+ } |
+ mLocationBarBackground.setAlpha(backgroundAlpha); |
+ |
+ if (mPhoneLocationBar.getAlpha() > 0 || mForceDrawLocationBarBackground) { |
+ mLocationBarBackground.setBounds( |
+ mUrlViewportBounds.left + mLocationBarBackgroundOffset.left, |
+ mUrlViewportBounds.top + mLocationBarBackgroundOffset.top, |
+ mUrlViewportBounds.right + mLocationBarBackgroundOffset.right, |
+ mUrlViewportBounds.bottom + mLocationBarBackgroundOffset.bottom); |
+ mLocationBarBackground.draw(canvas); |
+ } |
+ |
+ locationBarClipLeft = mUrlViewportBounds.left + mUrlBackgroundPadding.left |
+ + mLocationBarBackgroundOffset.left; |
+ locationBarClipRight = mUrlViewportBounds.right - mUrlBackgroundPadding.right |
+ + mLocationBarBackgroundOffset.right; |
+ |
+ // When unexpanded, the location bar's visible content boundaries are inset from the |
+ // viewport used to draw the background. During expansion transitions, compensation |
+ // is applied to increase the clip regions such that when the location bar converts |
+ // to the narrower collapsed layout that the visible content is the same. |
+ if (mUrlExpansionPercent != 1f) { |
+ int leftDelta = mUnfocusedLocationBarLayoutLeft - getViewBoundsLeftOfLocationBar(); |
+ int rightDelta = getViewBoundsRightOfLocationBar() |
+ - mUnfocusedLocationBarLayoutLeft |
+ - mUnfocusedLocationBarLayoutWidth; |
+ float inversePercent = 1f - mUrlExpansionPercent; |
+ locationBarClipLeft += leftDelta * inversePercent; |
+ locationBarClipRight -= rightDelta * inversePercent; |
+ } |
+ |
+ locationBarClipTop = mUrlViewportBounds.top + mUrlBackgroundPadding.top |
+ + mLocationBarBackgroundOffset.top; |
+ locationBarClipBottom = mUrlViewportBounds.bottom - mUrlBackgroundPadding.bottom |
+ + mLocationBarBackgroundOffset.bottom; |
+ // Clip the location bar child to the URL viewport calculated in onDraw. |
+ canvas.clipRect( |
+ locationBarClipLeft, locationBarClipTop, |
+ locationBarClipRight, locationBarClipBottom); |
+ clipped = true; |
+ } |
+ |
+ boolean retVal = super.drawChild(canvas, mPhoneLocationBar, drawingTime); |
+ |
+ if (clipped) canvas.restore(); |
+ return retVal; |
+ } |
+ |
+ @Override |
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
+ mBackgroundOverlayBounds.set(0, 0, w, mToolbarHeightWithoutShadow); |
+ if (mTabSwitcherAnimationBgOverlay != null) { |
+ mTabSwitcherAnimationBgOverlay.setBounds(0, 0, w, h); |
+ } |
+ super.onSizeChanged(w, h, oldw, oldh); |
+ } |
+ |
+ @Override |
+ protected void onAttachedToWindow() { |
+ super.onAttachedToWindow(); |
+ mToolbarShadow = (ImageView) getRootView().findViewById(R.id.toolbar_shadow); |
+ } |
+ |
+ @Override |
+ public void draw(Canvas canvas) { |
+ // If capturing a texture of the toolbar, ensure the alpha is set prior to draw(...) being |
+ // called. The alpha is being used prior to getting to draw(...), so updating the value |
+ // after this point was having no affect. |
+ if (mTextureCaptureMode) assert getAlpha() == 1f; |
+ if (!mTextureCaptureMode && mClipRect != null) { |
+ canvas.save(); |
+ canvas.clipRect(mClipRect); |
+ } |
+ super.draw(canvas); |
+ if (!mTextureCaptureMode && mClipRect != null) canvas.restore(); |
+ } |
+ |
+ @Override |
+ public void onStateRestored() { |
+ mToggleTabStackButton.setClickable(true); |
+ } |
+ |
+ @Override |
+ public boolean isReadyForTextureCapture() { |
+ return !(mInTabSwitcherMode || mTabSwitcherModeAnimation != null |
+ || urlHasFocus() || mUrlFocusChangeInProgress); |
+ } |
+ |
+ @Override |
+ protected void onNavigatedToDifferentPage() { |
+ super.onNavigatedToDifferentPage(); |
+ if (FeatureUtilities.isDocumentMode(getContext())) { |
+ mUrlContainer.setTrailingTextVisible(true); |
+ } |
+ } |
+ |
+ @Override |
+ public void setLoadProgress(int progress) { |
+ super.setLoadProgress(progress); |
+ if (FeatureUtilities.isDocumentMode(getContext()) && progress == 100) { |
+ mUrlContainer.setTrailingTextVisible(false); |
+ } |
+ } |
+ |
+ @Override |
+ public void finishAnimations() { |
+ mClipRect = null; |
+ if (mTabSwitcherModeAnimation != null) { |
+ mTabSwitcherModeAnimation.end(); |
+ mTabSwitcherModeAnimation = null; |
+ } |
+ if (mDelayedTabSwitcherModeAnimation != null) { |
+ mDelayedTabSwitcherModeAnimation.end(); |
+ mDelayedTabSwitcherModeAnimation = null; |
+ } |
+ } |
+ |
+ @Override |
+ public void getLocationBarContentRect(Rect outRect) { |
+ if (isLocationBarShownInNTP() && !isFocused()) { |
+ outRect.setEmpty(); |
+ return; |
+ } |
+ |
+ super.getLocationBarContentRect(outRect); |
+ } |
+ |
+ @Override |
+ protected void onHomeButtonUpdate(boolean homeButtonEnabled) { |
+ if (homeButtonEnabled) { |
+ mHomeButton.setVisibility(urlHasFocus() || mInTabSwitcherMode ? INVISIBLE : VISIBLE); |
+ if (!mBrowsingModeViews.contains(mHomeButton)) { |
+ mBrowsingModeViews.add(mHomeButton); |
+ } |
+ } else { |
+ mHomeButton.setVisibility(GONE); |
+ mBrowsingModeViews.remove(mHomeButton); |
+ } |
+ } |
+ |
+ private ObjectAnimator createEnterTabSwitcherModeAnimation() { |
+ ObjectAnimator enterAnimation = |
+ ObjectAnimator.ofFloat(this, mTabSwitcherModePercentProperty, 1.f); |
+ enterAnimation.setDuration(TAB_SWITCHER_MODE_ENTER_ANIMATION_DURATION_MS); |
+ enterAnimation.setInterpolator(new LinearInterpolator()); |
+ enterAnimation.addListener(new AnimatorListenerAdapter() { |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ // This is to deal with the view going invisible when resuming the activity and |
+ // running this animation. The view is still there and clickable but does not |
+ // render and only a layout triggers a refresh. See crbug.com/306890. |
+ if (!mToggleTabStackButton.isEnabled()) requestLayout(); |
+ } |
+ }); |
+ |
+ return enterAnimation; |
+ } |
+ |
+ private ObjectAnimator createExitTabSwitcherAnimation( |
+ final boolean animateNormalToolbar) { |
+ ObjectAnimator exitAnimation = |
+ ObjectAnimator.ofFloat(this, mTabSwitcherModePercentProperty, 0.f); |
+ exitAnimation.setDuration(animateNormalToolbar |
+ ? TAB_SWITCHER_MODE_EXIT_NORMAL_ANIMATION_DURATION_MS |
+ : TAB_SWITCHER_MODE_EXIT_FADE_ANIMATION_DURATION_MS); |
+ exitAnimation.setInterpolator(new LinearInterpolator()); |
+ exitAnimation.addListener(new AnimatorListenerAdapter() { |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ updateViewsForTabSwitcherMode(mInTabSwitcherMode); |
+ } |
+ }); |
+ |
+ return exitAnimation; |
+ } |
+ |
+ private ObjectAnimator createPostExitTabSwitcherAnimation() { |
+ ObjectAnimator exitAnimation = ObjectAnimator.ofFloat( |
+ this, View.TRANSLATION_Y, -getHeight(), 0.f); |
+ exitAnimation.setDuration(TAB_SWITCHER_MODE_POST_EXIT_ANIMATION_DURATION_MS); |
+ exitAnimation.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ exitAnimation.addListener(new AnimatorListenerAdapter() { |
+ @Override |
+ public void onAnimationStart(Animator animation) { |
+ updateViewsForTabSwitcherMode(mInTabSwitcherMode); |
+ // On older builds, force an update to ensure the new visuals are used |
+ // when bringing in the toolbar. crbug.com/404571 |
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) { |
+ requestLayout(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ mDelayedTabSwitcherModeAnimation = null; |
+ updateShadowVisibility(mInTabSwitcherMode); |
+ updateViewsForTabSwitcherMode(mInTabSwitcherMode); |
+ } |
+ }); |
+ |
+ return exitAnimation; |
+ } |
+ |
+ @Override |
+ public void setTextureCaptureMode(boolean textureMode) { |
+ assert mTextureCaptureMode != textureMode; |
+ mTextureCaptureMode = textureMode; |
+ if (mTextureCaptureMode) { |
+ mPreTextureCaptureAlpha = getAlpha(); |
+ setAlpha(1); |
+ } else { |
+ setAlpha(mPreTextureCaptureAlpha); |
+ mPreTextureCaptureAlpha = 1f; |
+ } |
+ } |
+ |
+ private boolean isTabSwitcherAnimationRunning() { |
+ return mUIAnimatingTabSwitcherTransition |
+ || (mTabSwitcherModeAnimation != null && mTabSwitcherModeAnimation.isRunning()) |
+ || (mDelayedTabSwitcherModeAnimation != null |
+ && mDelayedTabSwitcherModeAnimation.isRunning()); |
+ } |
+ |
+ private void updateViewsForTabSwitcherMode(boolean isInTabSwitcherMode) { |
+ int tabSwitcherViewsVisibility = isInTabSwitcherMode ? VISIBLE : INVISIBLE; |
+ int browsingViewsVisibility = isInTabSwitcherMode ? INVISIBLE : VISIBLE; |
+ |
+ for (View view : mTabSwitcherModeViews) { |
+ view.setVisibility(tabSwitcherViewsVisibility); |
+ } |
+ for (View view : mBrowsingModeViews) { |
+ view.setVisibility(browsingViewsVisibility); |
+ } |
+ getProgressBar().setVisibility( |
+ isInTabSwitcherMode || isTabSwitcherAnimationRunning() ? INVISIBLE : VISIBLE); |
+ updateVisualsForToolbarState(isInTabSwitcherMode); |
+ |
+ } |
+ |
+ @Override |
+ protected void setContentAttached(boolean attached) { |
+ updateVisualsForToolbarState(mInTabSwitcherMode); |
+ } |
+ |
+ @Override |
+ protected void setTabSwitcherMode( |
+ boolean inTabSwitcherMode, boolean showToolbar, boolean delayAnimation) { |
+ if (mInTabSwitcherMode == inTabSwitcherMode) return; |
+ |
+ finishAnimations(); |
+ |
+ mDelayingTabSwitcherAnimation = delayAnimation; |
+ |
+ if (inTabSwitcherMode) { |
+ if (mUrlFocusLayoutAnimator != null && mUrlFocusLayoutAnimator.isRunning()) { |
+ mUrlFocusLayoutAnimator.end(); |
+ mUrlFocusLayoutAnimator = null; |
+ } |
+ mNewTabButton.setEnabled(true); |
+ updateViewsForTabSwitcherMode(true); |
+ mTabSwitcherModeAnimation = createEnterTabSwitcherModeAnimation(); |
+ } else { |
+ if (!mDelayingTabSwitcherAnimation) { |
+ mTabSwitcherModeAnimation = createExitTabSwitcherAnimation(showToolbar); |
+ } |
+ mUIAnimatingTabSwitcherTransition = true; |
+ } |
+ |
+ mAnimateNormalToolbar = showToolbar; |
+ mInTabSwitcherMode = inTabSwitcherMode; |
+ if (mTabSwitcherModeAnimation != null) mTabSwitcherModeAnimation.start(); |
+ |
+ if (SysUtils.isLowEndDevice()) finishAnimations(); |
+ |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ @Override |
+ protected void onTabSwitcherTransitionFinished() { |
+ setAlpha(1.f); |
+ mClipRect = null; |
+ mUIAnimatingTabSwitcherTransition = false; |
+ if (!mAnimateNormalToolbar) { |
+ finishAnimations(); |
+ updateVisualsForToolbarState(mInTabSwitcherMode); |
+ } |
+ |
+ if (mDelayingTabSwitcherAnimation) { |
+ mDelayingTabSwitcherAnimation = false; |
+ mDelayedTabSwitcherModeAnimation = createPostExitTabSwitcherAnimation(); |
+ mDelayedTabSwitcherModeAnimation.start(); |
+ } else { |
+ updateViewsForTabSwitcherMode(mInTabSwitcherMode); |
+ } |
+ } |
+ |
+ private void updateOverlayDrawables() { |
+ if (!isNativeLibraryReady()) return; |
+ |
+ VisualState overlayState = computeVisualState(false); |
+ boolean visualStateChanged = mOverlayDrawablesVisualState != overlayState; |
+ |
+ if (!visualStateChanged && mVisualState == VisualState.BRAND_COLOR |
+ && getToolbarDataProvider().getPrimaryColor() |
+ != mTabSwitcherAnimationBgOverlay.getColor()) { |
+ visualStateChanged = true; |
+ } |
+ if (!visualStateChanged) return; |
+ |
+ mOverlayDrawablesVisualState = overlayState; |
+ mTabSwitcherAnimationBgOverlay.setColor(getToolbarColorForVisualState( |
+ mOverlayDrawablesVisualState)); |
+ |
+ if (shouldShowMenuButton()) { |
+ Resources res = getResources(); |
+ mTabSwitcherAnimationMenuDrawable = ApiCompatibilityUtils.getDrawable( |
+ res, R.drawable.btn_menu).mutate(); |
+ mTabSwitcherAnimationMenuDrawable.setColorFilter(isIncognito() ? Color.WHITE |
+ : getResources().getColor(R.color.light_normal_color), |
+ PorterDuff.Mode.SRC_IN); |
+ ((BitmapDrawable) mTabSwitcherAnimationMenuDrawable).setGravity(Gravity.CENTER); |
+ } |
+ } |
+ |
+ @Override |
+ public void setOnTabSwitcherClickHandler(OnClickListener listener) { |
+ mTabSwitcherListener = listener; |
+ } |
+ |
+ @Override |
+ public void setOnNewTabClickHandler(OnClickListener listener) { |
+ mNewTabListener = listener; |
+ } |
+ |
+ @Override |
+ public boolean shouldIgnoreSwipeGesture() { |
+ return super.shouldIgnoreSwipeGesture() || mUrlExpansionPercent > 0f; |
+ } |
+ |
+ private Property<TextView, Integer> buildUrlScrollProperty( |
+ final View containerView, final boolean isContainerRtl) { |
+ // If the RTL-ness of the container view changes during an animation, the scroll values |
+ // become invalid. If that happens, snap to the ending position and no longer update. |
+ return new Property<TextView, Integer>(Integer.class, "scrollX") { |
+ private boolean mRtlStateInvalid; |
+ |
+ @Override |
+ public Integer get(TextView view) { |
+ return view.getScrollX(); |
+ } |
+ |
+ @Override |
+ public void set(TextView view, Integer scrollX) { |
+ if (mRtlStateInvalid) return; |
+ boolean rtl = ApiCompatibilityUtils.isLayoutRtl(containerView); |
+ if (rtl != isContainerRtl) { |
+ mRtlStateInvalid = true; |
+ if (!rtl || mUrlBar.getLayout() != null) { |
+ scrollX = 0; |
+ if (rtl) { |
+ scrollX = (int) view.getLayout().getPrimaryHorizontal(0); |
+ scrollX -= view.getWidth(); |
+ } |
+ } |
+ } |
+ view.setScrollX(scrollX); |
+ } |
+ }; |
+ } |
+ |
+ private void populateUrlFocusingAnimatorSet(List<Animator> animators) { |
+ Animator animator = ObjectAnimator.ofFloat(this, mUrlFocusChangePercentProperty, 1f); |
+ animator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ |
+ for (int i = 0; i < mPhoneLocationBar.getChildCount(); i++) { |
+ View childView = mPhoneLocationBar.getChildAt(i); |
+ if (childView == mPhoneLocationBar.getFirstViewVisibleWhenFocused()) break; |
+ animator = ObjectAnimator.ofFloat(childView, ALPHA, 0); |
+ animator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ } |
+ |
+ float density = getContext().getResources().getDisplayMetrics().density; |
+ boolean isRtl = ApiCompatibilityUtils.isLayoutRtl(this); |
+ float toolbarButtonTranslationX = MathUtils.flipSignIf( |
+ URL_FOCUS_TOOLBAR_BUTTONS_TRANSLATION_X_DP, isRtl) * density; |
+ |
+ animator = ObjectAnimator.ofFloat( |
+ mMenuButton, TRANSLATION_X, toolbarButtonTranslationX); |
+ animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); |
+ animators.add(animator); |
+ |
+ animator = ObjectAnimator.ofFloat(mMenuButton, ALPHA, 0); |
+ animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); |
+ animators.add(animator); |
+ |
+ if (mToggleTabStackButton.getVisibility() != GONE) { |
+ animator = ObjectAnimator.ofFloat( |
+ mToggleTabStackButton, TRANSLATION_X, toolbarButtonTranslationX); |
+ animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); |
+ animators.add(animator); |
+ |
+ animator = ObjectAnimator.ofFloat(mToggleTabStackButton, ALPHA, 0); |
+ animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); |
+ animators.add(animator); |
+ } |
+ } |
+ |
+ private void populateUrlClearFocusingAnimatorSet(List<Animator> animators) { |
+ Animator animator = ObjectAnimator.ofFloat(this, mUrlFocusChangePercentProperty, 0f); |
+ animator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ |
+ animator = ObjectAnimator.ofFloat(mMenuButton, TRANSLATION_X, 0); |
+ animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setStartDelay(URL_CLEAR_FOCUS_MENU_DELAY_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ |
+ animator = ObjectAnimator.ofFloat(mMenuButton, ALPHA, 1); |
+ animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setStartDelay(URL_CLEAR_FOCUS_MENU_DELAY_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ |
+ if (mToggleTabStackButton.getVisibility() != GONE) { |
+ animator = ObjectAnimator.ofFloat(mToggleTabStackButton, TRANSLATION_X, 0); |
+ animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setStartDelay(URL_CLEAR_FOCUS_TABSTACK_DELAY_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ |
+ animator = ObjectAnimator.ofFloat(mToggleTabStackButton, ALPHA, 1); |
+ animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setStartDelay(URL_CLEAR_FOCUS_TABSTACK_DELAY_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ } |
+ |
+ for (int i = 0; i < mPhoneLocationBar.getChildCount(); i++) { |
+ View childView = mPhoneLocationBar.getChildAt(i); |
+ if (childView == mPhoneLocationBar.getFirstViewVisibleWhenFocused()) break; |
+ animator = ObjectAnimator.ofFloat(childView, ALPHA, 1); |
+ animator.setStartDelay(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS); |
+ animator.setDuration(URL_CLEAR_FOCUS_MENU_DELAY_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ } |
+ |
+ if (isLocationBarShownInNTP() && mNtpSearchBoxScrollPercent == 0f) return; |
+ |
+ if (!FeatureUtilities.isDocumentMode(getContext()) |
+ || mPhoneLocationBar.showingQueryInTheOmnibox()) { |
+ // The call to getLayout() can return null briefly during text changes, but as it |
+ // is only needed for RTL calculations, we proceed if the location bar is showing |
+ // LTR content. |
+ boolean isLocationBarRtl = ApiCompatibilityUtils.isLayoutRtl(mPhoneLocationBar); |
+ if (!isLocationBarRtl || mUrlBar.getLayout() != null) { |
+ int urlBarStartScrollX = 0; |
+ if (isLocationBarRtl) { |
+ urlBarStartScrollX = (int) mUrlBar.getLayout().getPrimaryHorizontal(0); |
+ urlBarStartScrollX -= mUrlBar.getWidth(); |
+ } |
+ |
+ // If the scroll position matches the current scroll position, do not trigger |
+ // this animation as it will cause visible jumps when going from cleared text |
+ // back to page URLs (despite it continually calling setScrollX with the same |
+ // number). |
+ if (mUrlBar.getScrollX() != urlBarStartScrollX) { |
+ animator = ObjectAnimator.ofInt( |
+ mUrlBar, |
+ buildUrlScrollProperty(mPhoneLocationBar, isLocationBarRtl), |
+ urlBarStartScrollX); |
+ animator.setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ animators.add(animator); |
+ } |
+ } |
+ } |
+ } |
+ |
+ @Override |
+ public void onUrlFocusChange(final boolean hasFocus) { |
+ super.onUrlFocusChange(hasFocus); |
+ |
+ triggerUrlFocusAnimation(hasFocus); |
+ |
+ TransitionDrawable shadowDrawable = (TransitionDrawable) mToolbarShadow.getDrawable(); |
+ if (hasFocus) { |
+ shadowDrawable.startTransition(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ } else { |
+ shadowDrawable.reverseTransition(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ } |
+ } |
+ |
+ private void triggerUrlFocusAnimation(final boolean hasFocus) { |
+ if (mUrlFocusLayoutAnimator != null && mUrlFocusLayoutAnimator.isRunning()) { |
+ mUrlFocusLayoutAnimator.cancel(); |
+ mUrlFocusLayoutAnimator = null; |
+ } |
+ |
+ List<Animator> animators = new ArrayList<Animator>(); |
+ if (hasFocus) { |
+ populateUrlFocusingAnimatorSet(animators); |
+ } else { |
+ populateUrlClearFocusingAnimatorSet(animators); |
+ } |
+ mUrlFocusLayoutAnimator = new AnimatorSet(); |
+ mUrlFocusLayoutAnimator.playTogether(animators); |
+ |
+ mUrlFocusChangeInProgress = true; |
+ mUrlFocusLayoutAnimator.addListener(new AnimatorListenerAdapter() { |
+ private boolean mCanceled; |
+ |
+ @Override |
+ public void onAnimationStart(Animator animation) { |
+ if (!hasFocus) { |
+ mDisableLocationBarRelayout = true; |
+ } else { |
+ mLayoutLocationBarInFocusedMode = true; |
+ requestLayout(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onAnimationCancel(Animator animation) { |
+ mCanceled = true; |
+ } |
+ |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ if (mCanceled) return; |
+ |
+ if (!hasFocus) { |
+ mDisableLocationBarRelayout = false; |
+ mLayoutLocationBarInFocusedMode = false; |
+ requestLayout(); |
+ } |
+ mPhoneLocationBar.finishUrlFocusChange(hasFocus); |
+ mUrlFocusChangeInProgress = false; |
+ } |
+ }); |
+ mUrlFocusLayoutAnimator.start(); |
+ } |
+ |
+ @Override |
+ protected boolean shouldShowMenuButton() { |
+ return !mPhoneLocationBar.showMenuButtonInOmnibox() && super.shouldShowMenuButton(); |
+ } |
+ |
+ @Override |
+ protected void updateTabCountVisuals(int numberOfTabs) { |
+ if (mToggleTabStackButton == null) return; |
+ mHomeButton.setEnabled(true); |
+ |
+ mToggleTabStackButton.setEnabled(numberOfTabs >= 1); |
+ mToggleTabStackButton.setContentDescription( |
+ getResources().getString(R.string.accessibility_toolbar_btn_tabswitcher_toggle, |
+ numberOfTabs)); |
+ mTabSwitcherButtonDrawableLight.updateForTabCount(numberOfTabs, isIncognito()); |
+ mTabSwitcherButtonDrawable.updateForTabCount(numberOfTabs, isIncognito()); |
+ |
+ boolean useTabStackDrawableLight = isIncognito(); |
+ if (mTabSwitcherAnimationTabStackDrawable == null |
+ || mIsOverlayTabStackDrawableLight != useTabStackDrawableLight) { |
+ mTabSwitcherAnimationTabStackDrawable = |
+ TabSwitcherDrawable.createTabSwitcherDrawable( |
+ getResources(), useTabStackDrawableLight); |
+ int[] stateSet = {android.R.attr.state_enabled}; |
+ mTabSwitcherAnimationTabStackDrawable.setState(stateSet); |
+ mTabSwitcherAnimationTabStackDrawable.setBounds( |
+ mToggleTabStackButton.getDrawable().getBounds()); |
+ mIsOverlayTabStackDrawableLight = useTabStackDrawableLight; |
+ } |
+ |
+ if (mTabSwitcherAnimationTabStackDrawable != null) { |
+ mTabSwitcherAnimationTabStackDrawable.updateForTabCount( |
+ numberOfTabs, isIncognito()); |
+ } |
+ } |
+ |
+ @Override |
+ protected void onTabContentViewChanged() { |
+ super.onTabContentViewChanged(); |
+ updateNtpAnimationState(); |
+ updateVisualsForToolbarState(mInTabSwitcherMode); |
+ } |
+ |
+ @Override |
+ protected void onTabOrModelChanged() { |
+ super.onTabOrModelChanged(); |
+ updateNtpAnimationState(); |
+ updateVisualsForToolbarState(mInTabSwitcherMode); |
+ } |
+ |
+ @Override |
+ protected void onPrimaryColorChanged() { |
+ super.onPrimaryColorChanged(); |
+ if (mBrandColorTransitionActive) mBrandColorTransitionAnimation.cancel(); |
+ final int initialColor = mToolbarBackground.getColor(); |
+ final int finalColor = getToolbarDataProvider().getPrimaryColor(); |
+ if (initialColor == finalColor) return; |
+ boolean shouldUseOpaque = BrandColorUtils.shouldUseOpaqueTextboxBackground(finalColor); |
+ final int initialAlpha = mUrlBackgroundAlpha; |
+ final int finalAlpha = |
+ shouldUseOpaque ? 255 : LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA; |
+ final boolean shouldAnimateAlpha = initialAlpha != finalAlpha; |
+ mBrandColorTransitionAnimation = ValueAnimator.ofFloat(0, 1) |
+ .setDuration(BRAND_COLOR_TRANSITION_DURATION_MS); |
+ mBrandColorTransitionAnimation.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); |
+ mBrandColorTransitionAnimation.addUpdateListener(new AnimatorUpdateListener() { |
+ @Override |
+ public void onAnimationUpdate(ValueAnimator animation) { |
+ float fraction = animation.getAnimatedFraction(); |
+ int red = (int) (Color.red(initialColor) |
+ + fraction * (Color.red(finalColor) - Color.red(initialColor))); |
+ int green = (int) (Color.green(initialColor) |
+ + fraction * (Color.green(finalColor) - Color.green(initialColor))); |
+ int blue = (int) (Color.blue(initialColor) |
+ + fraction * (Color.blue(finalColor) - Color.blue(initialColor))); |
+ if (shouldAnimateAlpha) { |
+ mUrlBackgroundAlpha = |
+ (int) (initialAlpha + fraction * (finalAlpha - initialAlpha)); |
+ } |
+ updateToolbarBackground(Color.rgb(red, green, blue)); |
+ } |
+ }); |
+ mBrandColorTransitionAnimation.addListener(new AnimatorListenerAdapter() { |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ mBrandColorTransitionActive = false; |
+ updateVisualsForToolbarState(mInTabSwitcherMode); |
+ } |
+ }); |
+ mBrandColorTransitionAnimation.start(); |
+ mBrandColorTransitionActive = true; |
+ } |
+ |
+ private void updateNtpAnimationState() { |
+ // Store previous NTP scroll before calling reset as that clears this value. |
+ boolean wasShowingNtp = mVisibleNewTabPage != null; |
+ float previousNtpScrollPercent = mNtpSearchBoxScrollPercent; |
+ |
+ resetNtpAnimationValues(); |
+ if (mVisibleNewTabPage != null) { |
+ mVisibleNewTabPage.setSearchBoxScrollListener(null); |
+ mVisibleNewTabPage = null; |
+ } |
+ mVisibleNewTabPage = getToolbarDataProvider().getNewTabPageForCurrentTab(); |
+ if (mVisibleNewTabPage != null && mVisibleNewTabPage.isLocationBarShownInNTP()) { |
+ mVisibleNewTabPage.setSearchBoxScrollListener(this); |
+ requestLayout(); |
+ } else if (wasShowingNtp) { |
+ // Convert the previous NTP scroll percentage to URL focus percentage because that |
+ // will give a nicer transition animation from the expanded NTP omnibox to the |
+ // collapsed normal omnibox on other non-NTP pages. |
+ if (previousNtpScrollPercent > 0f) { |
+ mUrlFocusChangePercent = |
+ Math.max(previousNtpScrollPercent, mUrlFocusChangePercent); |
+ triggerUrlFocusAnimation(false); |
+ } |
+ requestLayout(); |
+ } |
+ } |
+ |
+ @Override |
+ protected void onDefaultSearchEngineChanged() { |
+ super.onDefaultSearchEngineChanged(); |
+ // Post an update for the toolbar state, which will allow all other listeners |
+ // for the search engine change to update before we check on the state of the |
+ // world for a UI update. |
+ // TODO(tedchoc): Move away from updating based on the search engine change and instead |
+ // add the toolbar as a listener to the NewTabPage and udpate only when |
+ // it notifies the listeners that it has changed it's state. |
+ post(new Runnable() { |
+ @Override |
+ public void run() { |
+ updateVisualsForToolbarState(mInTabSwitcherMode); |
+ updateNtpAnimationState(); |
+ } |
+ }); |
+ } |
+ |
+ @Override |
+ protected void handleFindToolbarStateChange(boolean showing) { |
+ setVisibility(showing ? View.GONE : View.VISIBLE); |
+ TransitionDrawable shadowDrawable = (TransitionDrawable) mToolbarShadow.getDrawable(); |
+ if (showing) { |
+ shadowDrawable.startTransition(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ } else { |
+ shadowDrawable.reverseTransition(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS); |
+ } |
+ } |
+ |
+ private boolean isLocationBarShownInNTP() { |
+ NewTabPage ntp = getToolbarDataProvider().getNewTabPageForCurrentTab(); |
+ return ntp != null && ntp.isLocationBarShownInNTP(); |
+ } |
+ |
+ private void updateShadowVisibility(boolean isInTabSwitcherMode) { |
+ boolean shouldDrawShadow = !isInTabSwitcherMode && !isTabSwitcherAnimationRunning(); |
+ int shadowVisibility = shouldDrawShadow ? View.VISIBLE : View.INVISIBLE; |
+ |
+ if (mToolbarShadow.getVisibility() != shadowVisibility) { |
+ mToolbarShadow.setVisibility(shadowVisibility); |
+ } |
+ } |
+ |
+ private VisualState computeVisualState(boolean isInTabSwitcherMode) { |
+ if (isInTabSwitcherMode && isIncognito()) return VisualState.TAB_SWITCHER_INCOGNITO; |
+ if (isInTabSwitcherMode && !isIncognito()) return VisualState.TAB_SWITCHER_NORMAL; |
+ if (isLocationBarShownInNTP()) return VisualState.NEW_TAB_NORMAL; |
+ if (isIncognito()) return VisualState.INCOGNITO; |
+ if (getToolbarDataProvider().isUsingBrandColor()) return VisualState.BRAND_COLOR; |
+ return VisualState.NORMAL; |
+ } |
+ |
+ private void updateVisualsForToolbarState(boolean isInTabSwitcherMode) { |
+ if (mBrandColorTransitionActive) return; |
+ final boolean isIncognito = isIncognito(); |
+ |
+ VisualState visualState = computeVisualState(isInTabSwitcherMode); |
+ boolean visualStateChanged = mVisualState != visualState; |
+ |
+ int currentPrimaryColor = getToolbarDataProvider().getPrimaryColor(); |
+ if (mVisualState == VisualState.BRAND_COLOR && !visualStateChanged) { |
+ boolean useLightToolbarDrawables = |
+ BrandColorUtils.shouldUseLightDrawablesForToolbar(currentPrimaryColor); |
+ boolean unfocusedLocationBarUsesTransparentBg = |
+ !BrandColorUtils.shouldUseOpaqueTextboxBackground(currentPrimaryColor); |
+ if (useLightToolbarDrawables != mUseLightToolbarDrawables |
+ || unfocusedLocationBarUsesTransparentBg |
+ != mUnfocusedLocationBarUsesTransparentBg) { |
+ visualStateChanged = true; |
+ } else { |
+ updateToolbarBackground(VisualState.BRAND_COLOR); |
+ } |
+ } |
+ |
+ mVisualState = visualState; |
+ |
+ updateOverlayDrawables(); |
+ updateShadowVisibility(isInTabSwitcherMode); |
+ if (!visualStateChanged) { |
+ if (mVisualState == VisualState.NEW_TAB_NORMAL) { |
+ updateNtpTransitionAnimation( |
+ getToolbarDataProvider().getNewTabPageForCurrentTab()); |
+ } |
+ return; |
+ } |
+ |
+ mUseLightToolbarDrawables = false; |
+ mUnfocusedLocationBarUsesTransparentBg = false; |
+ mUrlBackgroundAlpha = 255; |
+ int progressBarResource = R.drawable.progress_bar; |
+ updateToolbarBackground(mVisualState); |
+ if (isInTabSwitcherMode) { |
+ mUseLightToolbarDrawables = true; |
+ mUrlBackgroundAlpha = LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA; |
+ progressBarResource = R.drawable.progress_bar_white; |
+ } else if (isIncognito()) { |
+ mUseLightToolbarDrawables = true; |
+ mUrlBackgroundAlpha = LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA; |
+ progressBarResource = R.drawable.progress_bar_white; |
+ } else if (mVisualState == VisualState.BRAND_COLOR) { |
+ mUseLightToolbarDrawables = |
+ BrandColorUtils.shouldUseLightDrawablesForToolbar(currentPrimaryColor); |
+ mUnfocusedLocationBarUsesTransparentBg = |
+ !BrandColorUtils.shouldUseOpaqueTextboxBackground(currentPrimaryColor); |
+ mUrlBackgroundAlpha = mUnfocusedLocationBarUsesTransparentBg |
+ ? LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA : 255; |
+ progressBarResource = mUseLightToolbarDrawables |
+ ? R.drawable.progress_bar_white : R.drawable.progress_bar; |
+ } else { |
+ mUseLightToolbarDrawables = false; |
+ mUrlBackgroundAlpha = 255; |
+ progressBarResource = R.drawable.progress_bar; |
+ } |
+ |
+ getProgressBar().setProgressDrawable( |
+ ApiCompatibilityUtils.getDrawable(getResources(), progressBarResource)); |
+ |
+ mToggleTabStackButton.setImageDrawable(mUseLightToolbarDrawables |
+ ? mTabSwitcherButtonDrawableLight : mTabSwitcherButtonDrawable); |
+ |
+ |
+ ColorStateList dark = getResources().getColorStateList(R.color.dark_mode_tint); |
+ ColorStateList white = getResources().getColorStateList(R.color.light_mode_tint); |
+ if (shouldShowMenuButton()) { |
+ mMenuButton.setTint(mUseLightToolbarDrawables ? white : dark); |
+ } |
+ if (mHomeButton.getVisibility() != GONE) { |
+ mHomeButton.setTint(mUseLightToolbarDrawables ? white : dark); |
+ } |
+ |
+ mPhoneLocationBar.updateVisualsForState(); |
+ |
+ // We update the alpha before comparing the visual state as we need to change |
+ // it's value when entering and exiting TabSwitcher mode. |
+ if (isLocationBarShownInNTP() && !isInTabSwitcherMode) { |
+ updateNtpTransitionAnimation( |
+ getToolbarDataProvider().getNewTabPageForCurrentTab()); |
+ } |
+ |
+ if (isInTabSwitcherMode) mNewTabButton.setIsIncognito(isIncognito); |
+ |
+ CharSequence newTabContentDescription = getResources().getText( |
+ isIncognito ? R.string.accessibility_toolbar_btn_new_incognito_tab : |
+ R.string.accessibility_toolbar_btn_new_tab); |
+ if (!newTabContentDescription.equals(mNewTabButton.getContentDescription())) { |
+ mNewTabButton.setContentDescription(newTabContentDescription); |
+ } |
+ } |
+ |
+ @Override |
+ public LocationBar getLocationBar() { |
+ return mPhoneLocationBar; |
+ } |
+} |
+ |