// 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.compositor.layouts.phone.stack;
import static org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.AnimatableAnimation.addAnimation;
import static org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab.Property.DISCARD_AMOUNT;
import static org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab.Property.SCROLL_OFFSET;
import android.view.animation.Interpolator;
import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
import org.chromium.chrome.browser.compositor.layouts.Layout.Orientation;
import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
import org.chromium.ui.interpolators.BakedBezierInterpolator;
/**
* A factory that builds animations for the tab stack.
*/
public abstract class StackAnimation {
public enum OverviewAnimationType {
ENTER_STACK,
NEW_TAB_OPENED,
TAB_FOCUSED,
VIEW_MORE,
REACH_TOP,
// Commit/uncommit tab discard animations
DISCARD,
DISCARD_ALL,
UNDISCARD,
// Start pinch animation un-tilt all the tabs.
START_PINCH,
// Special animation
FULL_ROLL,
// Used for when the current state of the system is not animating
NONE,
}
public static final float SCALE_AMOUNT = 0.90f;
protected static final float INITIAL_ALPHA_AMOUNT = 0.1f;
protected static final float INITIAL_SCALE_AMOUNT = 0.75f;
protected static final int ENTER_STACK_TOOLBAR_ALPHA_DURATION = 100;
protected static final int ENTER_STACK_TOOLBAR_ALPHA_DELAY = 100;
protected static final int ENTER_STACK_ANIMATION_DURATION = 300;
protected static final int ENTER_STACK_RESIZE_DELAY = 10;
protected static final int ENTER_STACK_BORDER_ALPHA_DURATION = 200;
protected static final int ENTER_STACK_BORDER_ALPHA_DELAY = 0;
protected static final float ENTER_STACK_SIZE_RATIO = 0.35f;
protected static final int TAB_FOCUSED_TOOLBAR_ALPHA_DURATION = 250;
protected static final int TAB_FOCUSED_TOOLBAR_ALPHA_DELAY = 0;
protected static final int TAB_FOCUSED_ANIMATION_DURATION = 400;
protected static final int TAB_FOCUSED_Y_STACK_DURATION = 200;
protected static final int TAB_FOCUSED_BORDER_ALPHA_DURATION = 200;
protected static final int TAB_FOCUSED_BORDER_ALPHA_DELAY = 0;
protected static final int TAB_FOCUSED_MAX_DELAY = 100;
protected static final int VIEW_MORE_ANIMATION_DURATION = 400;
protected static final float VIEW_MORE_SIZE_RATIO = 0.75f;
protected static final int VIEW_MORE_MIN_SIZE = 200;
protected static final int REACH_TOP_ANIMATION_DURATION = 400;
protected static final int UNDISCARD_ANIMATION_DURATION = 150;
protected static final int TAB_OPENED_ANIMATION_DURATION = 300;
protected static final int TAB_OPENED_BORDER_ALPHA_DURATION = 100;
protected static final int TAB_OPENED_BORDER_ALPHA_DELAY = 100;
protected static final int DISCARD_ANIMATION_DURATION = 150;
protected static final int TAB_REORDER_DURATION = 500;
protected static final int TAB_REORDER_START_SPAN = 400;
protected static final int START_PINCH_ANIMATION_DURATION = 75;
protected static final int FULL_ROLL_ANIMATION_DURATION = 1000;
protected final float mWidth;
protected final float mHeight;
protected final float mHeightMinusTopControls;
protected final float mBorderTopHeight;
protected final float mBorderTopOpaqueHeight;
protected final float mBorderLeftWidth;
/**
* Protected constructor.
*
* @param width The width of the layout in dp.
* @param height The height of the layout in dp.
* @param heightMinusTopControls The height of the layout minus the top controls in dp.
* @param borderFramePaddingTop The top padding of the border frame in dp.
* @param borderFramePaddingTopOpaque The opaque top padding of the border frame in dp.
* @param borderFramePaddingLeft The left padding of the border frame in dp.
*/
protected StackAnimation(float width, float height, float heightMinusTopControls,
float borderFramePaddingTop, float borderFramePaddingTopOpaque,
float borderFramePaddingLeft) {
mWidth = width;
mHeight = height;
mHeightMinusTopControls = heightMinusTopControls;
mBorderTopHeight = borderFramePaddingTop;
mBorderTopOpaqueHeight = borderFramePaddingTopOpaque;
mBorderLeftWidth = borderFramePaddingLeft;
}
/**
* The factory method that creates the particular factory method based on the orientation
* parameter.
*
* @param width The width of the layout in dp.
* @param height The height of the layout in dp.
* @param heightMinusTopControls The height of the layout minus the top controls in dp.
* @param borderFramePaddingTop The top padding of the border frame in dp.
* @param borderFramePaddingTopOpaque The opaque top padding of the border frame in dp.
* @param borderFramePaddingLeft The left padding of the border frame in dp.
* @param orientation The orientation that will be used to create the
* appropriate {@link StackAnimation}.
* @return The TabSwitcherAnimationFactory instance.
*/
public static StackAnimation createAnimationFactory(float width, float height,
float heightMinusTopControls, float borderFramePaddingTop,
float borderFramePaddingTopOpaque, float borderFramePaddingLeft, int orientation) {
StackAnimation factory = null;
switch (orientation) {
case Orientation.LANDSCAPE:
factory = new StackAnimationLandscape(width, height, heightMinusTopControls,
borderFramePaddingTop, borderFramePaddingTopOpaque, borderFramePaddingLeft);
break;
case Orientation.PORTRAIT:
default:
factory = new StackAnimationPortrait(width, height, heightMinusTopControls,
borderFramePaddingTop, borderFramePaddingTopOpaque, borderFramePaddingLeft);
break;
}
return factory;
}
/**
* The wrapper method responsible for delegating the animations request to the appropriate
* helper method. Not all parameters are used for each request.
*
* @param type The type of animation to be created. This is what
* determines which helper method is called.
* @param tabs The tabs that make up the current stack that will
* be animated.
* @param focusIndex The index of the tab that is the focus of this animation.
* @param sourceIndex The index of the tab that triggered this animation.
* @param spacing The default spacing between the tabs.
* @param scrollOffset The scroll offset in the current orientation.
* @param warpSize The warp size of the transform from scroll space to screen space.
* @param discardRange The range of the discard amount value.
* @return The resulting TabSwitcherAnimation that will animate the tabs.
*/
public ChromeAnimation<?> createAnimatorSetForType(OverviewAnimationType type, StackTab[] tabs,
int focusIndex, int sourceIndex, int spacing, float scrollOffset, float warpSize,
float discardRange) {
ChromeAnimation<?> set = null;
if (tabs != null) {
switch (type) {
case ENTER_STACK:
set = createEnterStackAnimatorSet(tabs, focusIndex, spacing, warpSize);
break;
case TAB_FOCUSED:
set = createTabFocusedAnimatorSet(tabs, focusIndex, spacing, warpSize);
break;
case VIEW_MORE:
set = createViewMoreAnimatorSet(tabs, sourceIndex);
break;
case REACH_TOP:
set = createReachTopAnimatorSet(tabs, warpSize);
break;
case DISCARD:
// Purposeful fall through
case DISCARD_ALL:
// Purposeful fall through
case UNDISCARD:
set = createUpdateDiscardAnimatorSet(tabs, spacing, warpSize, discardRange);
break;
case NEW_TAB_OPENED:
set = createNewTabOpenedAnimatorSet(tabs, focusIndex, discardRange);
break;
case START_PINCH:
set = createStartPinchAnimatorSet(tabs);
break;
case FULL_ROLL:
set = createFullRollAnimatorSet(tabs);
break;
case NONE:
break;
}
}
return set;
}
protected abstract float getScreenSizeInScrollDirection();
protected abstract float getScreenPositionInScrollDirection(StackTab tab);
protected abstract void addTiltScrollAnimation(ChromeAnimation<Animatable<?>> set,
LayoutTab tab, float end, int duration, int startTime);
/**
* @return The direction the tab should come from as it is created. -1 means top/right, 1 means
* bottom/left.
*/
protected abstract int getTabCreationDirection();
/**
* Responsible for generating the animations that shows the stack
* being entered.
*
* @param tabs The tabs that make up the stack. These are the
* tabs that will be affected by the TabSwitcherAnimation.
* @param focusIndex The focused index. In this case, this is the index of
* the tab that was being viewed before entering the stack.
* @param spacing The default spacing between tabs.
* @param warpSize The warp size of the transform from scroll space to screen space.
* @return The TabSwitcherAnimation instance that will tween the
* tabs to create the appropriate animation.
*/
protected abstract ChromeAnimation<?> createEnterStackAnimatorSet(
StackTab[] tabs, int focusIndex, int spacing, float warpSize);
/**
* Responsible for generating the animations that shows a tab being
* focused (the stack is being left).
*
* @param tabs The tabs that make up the stack. These are the
* tabs that will be affected by the TabSwitcherAnimation.
* @param focusIndex The focused index. In this case, this is the index of
* the tab clicked and is being brought up to view.
* @param spacing The default spacing between tabs.
* @param warpSize The warp size of the transform from scroll space to screen space.
* @return The TabSwitcherAnimation instance that will tween the
* tabs to create the appropriate animation.
*/
protected abstract ChromeAnimation<?> createTabFocusedAnimatorSet(
StackTab[] tabs, int focusIndex, int spacing, float warpSize);
/**
* Responsible for generating the animations that Shows more of the selected tab.
*
* @param tabs The tabs that make up the stack. These are the
* tabs that will be affected by the TabSwitcherAnimation.
* @param selectedIndex The selected index. In this case, this is the index of
* the tab clicked and is being brought up to view.
* @return The TabSwitcherAnimation instance that will tween the
* tabs to create the appropriate animation.
*/
protected abstract ChromeAnimation<?> createViewMoreAnimatorSet(
StackTab[] tabs, int selectedIndex);
/**
* Responsible for generating the TabSwitcherAnimation that moves the tabs up so they
* reach the to top the screen.
*
* @param tabs The tabs that make up the stack. These are the
* tabs that will be affected by the TabSwitcherAnimation.
* @param warpSize The warp size of the transform from scroll space to screen space.
* @return The TabSwitcherAnimation instance that will tween the
* tabs to create the appropriate animation.
*/
protected abstract ChromeAnimation<?> createReachTopAnimatorSet(
StackTab[] tabs, float warpSize);
/**
* Responsible for generating the animations that moves the tabs back in from
* discard attempt or commit the current discard (if any). It also re-even the tabs
* if one of then is removed.
*
* @param tabs The tabs that make up the stack. These are the
* tabs that will be affected by the TabSwitcherAnimation.
* @param spacing The default spacing between tabs.
* @param warpSize The warp size of the transform from scroll space to screen space.
* @param discardRange The maximum value the discard amount.
* @return The TabSwitcherAnimation instance that will tween the
* tabs to create the appropriate animation.
*/
protected ChromeAnimation<?> createUpdateDiscardAnimatorSet(
StackTab[] tabs, int spacing, float warpSize, float discardRange) {
ChromeAnimation<Animatable<?>> set = new ChromeAnimation<Animatable<?>>();
int dyingTabsCount = 0;
float firstDyingTabOffset = 0;
for (int i = 0; i < tabs.length; ++i) {
StackTab tab = tabs[i];
addTiltScrollAnimation(set, tab.getLayoutTab(), 0.0f, UNDISCARD_ANIMATION_DURATION, 0);
if (tab.isDying()) {
dyingTabsCount++;
if (dyingTabsCount == 1) {
firstDyingTabOffset = getScreenPositionInScrollDirection(tab);
}
}
}
Interpolator interpolator = BakedBezierInterpolator.FADE_OUT_CURVE;
int newIndex = 0;
for (int i = 0; i < tabs.length; ++i) {
StackTab tab = tabs[i];
long startTime = (long) Math.max(0, TAB_REORDER_START_SPAN
/ getScreenSizeInScrollDirection()
* (getScreenPositionInScrollDirection(tab) - firstDyingTabOffset));
if (tab.isDying()) {
float discard = tab.getDiscardAmount();
if (discard == 0.0f) discard = isDefaultDiscardDirectionPositive() ? 0.0f : -0.0f;
float s = Math.copySign(1.0f, discard);
long duration = (long) (DISCARD_ANIMATION_DURATION
* (1.0f - Math.abs(discard / discardRange)));
addAnimation(set, tab, DISCARD_AMOUNT, discard, discardRange * s, duration,
startTime, false, interpolator);
} else {
if (tab.getDiscardAmount() != 0.f) {
addAnimation(set, tab, DISCARD_AMOUNT, tab.getDiscardAmount(), 0.0f,
UNDISCARD_ANIMATION_DURATION, 0);
}
float newScrollOffset = StackTab.screenToScroll(spacing * newIndex, warpSize);
// If the tab is not dying we want to readjust it's position
// based on the new spacing requirements. For a fully discarded tab, just
// put it in the right place.
if (tab.getDiscardAmount() >= discardRange) {
tab.setScrollOffset(newScrollOffset);
tab.setScale(SCALE_AMOUNT);
} else {
float start = tab.getScrollOffset();
if (start != newScrollOffset) {
addAnimation(set, tab, SCROLL_OFFSET, start, newScrollOffset,
TAB_REORDER_DURATION, startTime);
}
}
newIndex++;
}
}
return set;
}
/**
* This is used to determine the discard direction when user just clicks X to close a tab.
* On portrait, positive direction (x) is right hand side.
* On landscape, positive direction (y) is towards bottom.
* @return True, if default discard direction is positive.
*/
protected abstract boolean isDefaultDiscardDirectionPositive();
/**
* Responsible for generating the animations that shows a new tab being opened.
*
* @param tabs The tabs that make up the stack. These are the
* tabs that will be affected by the TabSwitcherAnimation.
* @param focusIndex The focused index. In this case, this is the index of
* the tab that was just created.
* @param discardRange The maximum value the discard amount.
* @return The TabSwitcherAnimation instance that will tween the
* tabs to create the appropriate animation.
*/
// TODO(dtrainor): Remove this after confirming nothing uses this.
protected ChromeAnimation<?> createNewTabOpenedAnimatorSet(
StackTab[] tabs, int focusIndex, float discardRange) {
if (focusIndex < 0 || focusIndex >= tabs.length) return null;
ChromeAnimation<Animatable<?>> set = new ChromeAnimation<Animatable<?>>();
StackTab tab = tabs[focusIndex];
tab.getLayoutTab().setVisible(false);
tab.setXInStackInfluence(0.0f);
tab.setYInStackInfluence(0.0f);
tab.setDiscardFromClick(true);
tab.setDiscardOriginX(tab.getLayoutTab().getOriginalContentWidth());
tab.setDiscardOriginY(tab.getLayoutTab().getOriginalContentHeight() / 2.f);
tab.getLayoutTab().setAlpha(0.0f);
tab.getLayoutTab().setBorderAlpha(0.0f);
addAnimation(set, tab, DISCARD_AMOUNT, getTabCreationDirection() * discardRange, 0.0f,
TAB_OPENED_ANIMATION_DURATION, 0, false,
ChromeAnimation.getAccelerateInterpolator());
return set;
}
/**
* Responsible for generating the animations that flattens tabs when a pinch begins.
*
* @param tabs The tabs that make up the stack. These are the tabs that will
* be affected by the animations.
* @return The TabSwitcherAnimation instance that will tween the tabs to
* create the appropriate animation.
*/
protected ChromeAnimation<?> createStartPinchAnimatorSet(StackTab[] tabs) {
ChromeAnimation<Animatable<?>> set = new ChromeAnimation<Animatable<?>>();
for (int i = 0; i < tabs.length; ++i) {
addTiltScrollAnimation(
set, tabs[i].getLayoutTab(), 0, START_PINCH_ANIMATION_DURATION, 0);
}
return set;
}
/**
* Responsible for generating the animations that make all the tabs do a full roll.
*
* @param tabs The tabs that make up the stack. These are the tabs that will be affected by the
* animations.
* @return The TabSwitcherAnimation instance that will tween the tabs to create the
* appropriate animation.
*/
protected ChromeAnimation<?> createFullRollAnimatorSet(StackTab[] tabs) {
ChromeAnimation<Animatable<?>> set = new ChromeAnimation<Animatable<?>>();
for (int i = 0; i < tabs.length; ++i) {
LayoutTab layoutTab = tabs[i].getLayoutTab();
// Set the pivot
layoutTab.setTiltX(layoutTab.getTiltX(), layoutTab.getScaledContentHeight() / 2.0f);
layoutTab.setTiltY(layoutTab.getTiltY(), layoutTab.getScaledContentWidth() / 2.0f);
// Create the angle animation
addTiltScrollAnimation(set, layoutTab, -360.0f, FULL_ROLL_ANIMATION_DURATION, 0);
}
return set;
}
/**
* @return The offset for the toolbar to line the top up with the opaque component of the
* border.
*/
protected float getToolbarOffsetToLineUpWithBorder() {
final float toolbarHeight = mHeight - mHeightMinusTopControls;
return toolbarHeight - mBorderTopOpaqueHeight;
}
}