// 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.content.Context; import android.util.TypedValue; import android.view.View; import android.widget.TextView; import org.chromium.chrome.R; import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener; import org.chromium.ui.interpolators.BakedBezierInterpolator; /** * A delegate class to handle the title animation and security icon fading animation in * {@link CustomTabToolbar}. * <p> * How does the title animation work? * <p> * 1. The title bar is set from {@link View#GONE} to {@link View#VISIBLE}, which triggers a relayout * of the location bar. 2. On relayout, the newly positioned urlbar will be moved&scaled to look * exactly the same as before the relayout. (Note the scale factor is calculated based on * {@link TextView#getTextSize()}, not height or width.) 3. Finally the urlbar will be animated to * its new position. */ class CustomTabToolbarAnimationDelegate { private static final int CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS = 200; private static final int CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS = 150; private final View mSecurityButton; private final View mTitleUrlContainer; private final AnimatorSet mSecurityButtonShowAnimator; private final AnimatorSet mSecurityButtonHideAnimator; private TextView mUrlBar; private TextView mTitleBar; private int mSecurityButtonWidth; // A flag controlling whether the animation has run before. private boolean mShouldRunTitleAnimation; /** * Constructs an instance of {@link CustomTabToolbarAnimationDelegate}. */ CustomTabToolbarAnimationDelegate(View securityButton, final View titleUrlContainer) { mSecurityButton = securityButton; mTitleUrlContainer = titleUrlContainer; mSecurityButtonWidth = securityButton.getResources() .getDimensionPixelSize(R.dimen.location_bar_icon_width); titleUrlContainer.setTranslationX(-mSecurityButtonWidth); mSecurityButtonShowAnimator = new AnimatorSet(); Animator translateRight = ObjectAnimator.ofFloat(titleUrlContainer, View.TRANSLATION_X, 0); translateRight.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); translateRight.setDuration(CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS); Animator fadeIn = ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 1); fadeIn.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE); fadeIn.setDuration(CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS); fadeIn.addListener(new CancelAwareAnimatorListener() { @Override public void onStart(Animator animation) { mSecurityButton.setVisibility(View.VISIBLE); } }); mSecurityButtonShowAnimator.playSequentially(translateRight, fadeIn); mSecurityButtonHideAnimator = new AnimatorSet(); Animator fadeOut = ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 0); fadeOut.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); fadeOut.setDuration(CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS); fadeOut.addListener(new CancelAwareAnimatorListener() { @Override public void onEnd(Animator animation) { mSecurityButton.setVisibility(View.INVISIBLE); } }); Animator translateLeft = ObjectAnimator.ofFloat(titleUrlContainer, View.TRANSLATION_X, -mSecurityButtonWidth); translateLeft.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE); translateLeft.setDuration(CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS); mSecurityButtonHideAnimator.playSequentially(fadeOut, translateLeft); } /** * Sets whether the title scaling animation is enabled. */ void setTitleAnimationEnabled(boolean enabled) { mShouldRunTitleAnimation = enabled; } void prepareTitleAnim(TextView urlBar, TextView titleBar) { mTitleBar = titleBar; mUrlBar = urlBar; mUrlBar.setPivotX(0f); mUrlBar.setPivotY(0f); mShouldRunTitleAnimation = true; } /** * Starts animation for urlbar scaling and title fading-in. If this animation has already run * once, does nothing. */ void startTitleAnimation(Context context) { if (!mShouldRunTitleAnimation) return; mShouldRunTitleAnimation = false; mTitleBar.setVisibility(View.VISIBLE); mTitleBar.setAlpha(0f); float newSizeSp = context.getResources().getDimension(R.dimen.custom_tabs_url_text_size); float oldSizePx = mUrlBar.getTextSize(); mUrlBar.setTextSize(TypedValue.COMPLEX_UNIT_PX, newSizeSp); float newSizePx = mUrlBar.getTextSize(); final float scale = oldSizePx / newSizePx; // View#getY() cannot be used because the boundary of the parent will change after relayout. final int[] oldLoc = new int[2]; mUrlBar.getLocationInWindow(oldLoc); mUrlBar.requestLayout(); mUrlBar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { mUrlBar.removeOnLayoutChangeListener(this); int[] newLoc = new int[2]; mUrlBar.getLocationInWindow(newLoc); mUrlBar.setScaleX(scale); mUrlBar.setScaleY(scale); mUrlBar.setTranslationX(oldLoc[0] - newLoc[0]); mUrlBar.setTranslationY(oldLoc[1] - newLoc[1]); mUrlBar.animate().scaleX(1f).scaleY(1f).translationX(0).translationY(0) .setDuration(CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS) .setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mTitleBar.animate().alpha(1f) .setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE) .setDuration(CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS).start(); } }).start(); } }); } /** * Starts the animation to show the security button. */ void showSecurityButton() { if (mSecurityButtonHideAnimator.isStarted()) mSecurityButtonHideAnimator.cancel(); if (mSecurityButtonShowAnimator.isStarted() || mSecurityButton.getVisibility() == View.VISIBLE) { return; } mSecurityButtonShowAnimator.start(); } /** * Starts the animation to hide the security button. */ void hideSecurityButton() { if (mSecurityButtonShowAnimator.isStarted()) mSecurityButtonShowAnimator.cancel(); if (mSecurityButtonHideAnimator.isStarted() || mTitleUrlContainer.getTranslationX() == -mSecurityButtonWidth) { return; } mSecurityButtonHideAnimator.start(); } }