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

Unified Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
+ }
+}
+

Powered by Google App Engine
This is Rietveld 408576698