| Index: chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
|
| diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..274b2b9da6e4de8f6c980771e16502260eb529ad
|
| --- /dev/null
|
| +++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
|
| @@ -0,0 +1,414 @@
|
| +// 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;
|
| +
|
| +import android.content.Context;
|
| +import android.graphics.Rect;
|
| +import android.view.animation.Interpolator;
|
| +
|
| +import org.chromium.chrome.browser.compositor.LayerTitleCache;
|
| +import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
|
| +import org.chromium.chrome.browser.compositor.layouts.Layout;
|
| +import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
|
| +import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
|
| +import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
|
| +import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
|
| +import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
|
| +import org.chromium.chrome.browser.compositor.layouts.phone.stack.Stack;
|
| +import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation;
|
| +import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
|
| +import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer;
|
| +import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
|
| +import org.chromium.chrome.browser.tabmodel.TabModel;
|
| +import org.chromium.ui.interpolators.BakedBezierInterpolator;
|
| +import org.chromium.ui.resources.ResourceManager;
|
| +
|
| +import java.util.Arrays;
|
| +import java.util.LinkedList;
|
| +
|
| +/**
|
| + * This class handles animating the opening of new tabs.
|
| + */
|
| +public class SimpleAnimationLayout
|
| + extends Layout implements Animatable<SimpleAnimationLayout.Property> {
|
| + /**
|
| + * Animation properties
|
| + */
|
| + public enum Property { DISCARD_AMOUNT }
|
| +
|
| + /** Duration of the first step of the background animation: zooming out, rotating in */
|
| + private static final long BACKGROUND_STEP1_DURATION = 300;
|
| + /** Duration of the second step of the background animation: pause */
|
| + private static final long BACKGROUND_STEP2_DURATION = 150;
|
| + /** Duration of the third step of the background animation: zooming in, sliding out */
|
| + private static final long BACKGROUND_STEP3_DURATION = 300;
|
| + /** Start time offset of the third step of the background animation */
|
| + private static final long BACKGROUND_STEP3_START =
|
| + BACKGROUND_STEP1_DURATION + BACKGROUND_STEP2_DURATION;
|
| + /** Percentage of the screen covered by the new tab */
|
| + private static final float BACKGROUND_COVER_PCTG = 0.5f;
|
| +
|
| + /** The time duration of the animation */
|
| + protected static final int FOREGROUND_ANIMATION_DURATION = 300;
|
| +
|
| + /** The time duration of the animation */
|
| + protected static final int TAB_CLOSED_ANIMATION_DURATION = 250;
|
| +
|
| + /**
|
| + * A cached {@link LayoutTab} representation of the currently closing tab. If it's not
|
| + * null, it means tabClosing() has been called to start animation setup but
|
| + * tabClosed() has not yet been called to finish animation startup
|
| + */
|
| + private LayoutTab mClosedTab;
|
| +
|
| + private LayoutTab mAnimatedTab;
|
| + private final TabListSceneLayer mSceneLayer;
|
| +
|
| + /**
|
| + * Creates an instance of the {@link SimpleAnimationLayout}.
|
| + * @param context The current Android's context.
|
| + * @param updateHost The {@link LayoutUpdateHost} view for this layout.
|
| + * @param renderHost The {@link LayoutRenderHost} view for this layout.
|
| + * @param eventFilter The {@link EventFilter} that is needed for this view.
|
| + */
|
| + public SimpleAnimationLayout(Context context, LayoutUpdateHost updateHost,
|
| + LayoutRenderHost renderHost, EventFilter eventFilter) {
|
| + super(context, updateHost, renderHost, eventFilter);
|
| + mSceneLayer = new TabListSceneLayer();
|
| + }
|
| +
|
| + @Override
|
| + public int getSizingFlags() {
|
| + return SizingFlags.HELPER_SUPPORTS_FULLSCREEN;
|
| + }
|
| +
|
| + @Override
|
| + public void show(long time, boolean animate) {
|
| + super.show(time, animate);
|
| + reset();
|
| + }
|
| +
|
| + @Override
|
| + public boolean handlesTabCreating() {
|
| + return true;
|
| + }
|
| +
|
| + @Override
|
| + public boolean handlesTabClosing() {
|
| + return true;
|
| + }
|
| +
|
| + @Override
|
| + protected void updateLayout(long time, long dt) {
|
| + super.updateLayout(time, dt);
|
| + if (mLayoutTabs == null) return;
|
| + boolean needUpdate = false;
|
| + for (int i = mLayoutTabs.length - 1; i >= 0; i--) {
|
| + needUpdate = mLayoutTabs[i].updateSnap(dt) || needUpdate;
|
| + }
|
| + if (needUpdate) requestUpdate();
|
| + }
|
| +
|
| + @Override
|
| + public void onTabCreating(int sourceTabId) {
|
| + super.onTabCreating(sourceTabId);
|
| + reset();
|
| +
|
| + // Make sure any currently running animations can't influence tab if we are reusing it.
|
| + forceAnimationToFinish();
|
| +
|
| + ensureSourceTabCreated(sourceTabId);
|
| + }
|
| +
|
| + private void ensureSourceTabCreated(int sourceTabId) {
|
| + if (mLayoutTabs != null && mLayoutTabs.length == 1
|
| + && mLayoutTabs[0].getId() == sourceTabId) {
|
| + return;
|
| + }
|
| + // Just draw the source tab on the screen.
|
| + TabModel sourceModel = mTabModelSelector.getModelForTabId(sourceTabId);
|
| + if (sourceModel == null) return;
|
| + LayoutTab sourceLayoutTab =
|
| + createLayoutTab(sourceTabId, sourceModel.isIncognito(), NO_CLOSE_BUTTON, NO_TITLE);
|
| + sourceLayoutTab.setBorderAlpha(0.0f);
|
| +
|
| + mLayoutTabs = new LayoutTab[] {sourceLayoutTab};
|
| + updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(sourceTabId)));
|
| + }
|
| +
|
| + @Override
|
| + public void onTabCreated(long time, int id, int index, int sourceId, boolean newIsIncognito,
|
| + boolean background, float originX, float originY) {
|
| + super.onTabCreated(time, id, index, sourceId, newIsIncognito, background, originX, originY);
|
| + ensureSourceTabCreated(sourceId);
|
| + if (background && mLayoutTabs != null && mLayoutTabs.length > 0) {
|
| + tabCreatedInBackground(id, sourceId, newIsIncognito, originX, originY);
|
| + } else {
|
| + tabCreatedInForeground(id, sourceId, newIsIncognito, originX, originY);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Animate opening a tab in the foreground.
|
| + *
|
| + * @param id The id of the new tab to animate.
|
| + * @param sourceId The id of the tab that spawned this new tab.
|
| + * @param newIsIncognito true if the new tab is an incognito tab.
|
| + * @param originX The X coordinate of the last touch down event that spawned this tab.
|
| + * @param originY The Y coordinate of the last touch down event that spawned this tab.
|
| + */
|
| + private void tabCreatedInForeground(
|
| + int id, int sourceId, boolean newIsIncognito, float originX, float originY) {
|
| + LayoutTab newLayoutTab = createLayoutTab(id, newIsIncognito, NO_CLOSE_BUTTON, NO_TITLE);
|
| + if (mLayoutTabs == null || mLayoutTabs.length == 0) {
|
| + mLayoutTabs = new LayoutTab[] {newLayoutTab};
|
| + } else {
|
| + mLayoutTabs = new LayoutTab[] {mLayoutTabs[0], newLayoutTab};
|
| + }
|
| + updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(id, sourceId)));
|
| +
|
| + newLayoutTab.setBorderAlpha(0.0f);
|
| + newLayoutTab.setStaticToViewBlend(1.f);
|
| +
|
| + forceAnimationToFinish();
|
| +
|
| + Interpolator interpolator = BakedBezierInterpolator.TRANSFORM_CURVE;
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.SCALE, 0.f, 1.f,
|
| + FOREGROUND_ANIMATION_DURATION, 0, false, interpolator);
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.ALPHA, 0.f, 1.f,
|
| + FOREGROUND_ANIMATION_DURATION, 0, false, interpolator);
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.X, originX, 0.f,
|
| + FOREGROUND_ANIMATION_DURATION, 0, false, interpolator);
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.Y, originY, 0.f,
|
| + FOREGROUND_ANIMATION_DURATION, 0, false, interpolator);
|
| +
|
| + mTabModelSelector.selectModel(newIsIncognito);
|
| + startHiding(id, false);
|
| + }
|
| +
|
| + /**
|
| + * Animate opening a tab in the background.
|
| + *
|
| + * @param id The id of the new tab to animate.
|
| + * @param sourceId The id of the tab that spawned this new tab.
|
| + * @param newIsIncognito true if the new tab is an incognito tab.
|
| + * @param originX The X screen coordinate in dp of the last touch down event that spawned
|
| + * this tab.
|
| + * @param originY The Y screen coordinate in dp of the last touch down event that spawned
|
| + * this tab.
|
| + */
|
| + private void tabCreatedInBackground(
|
| + int id, int sourceId, boolean newIsIncognito, float originX, float originY) {
|
| + LayoutTab newLayoutTab = createLayoutTab(id, newIsIncognito, NO_CLOSE_BUTTON, NEED_TITLE);
|
| + // mLayoutTabs should already have the source tab from tabCreating().
|
| + assert mLayoutTabs.length == 1;
|
| + LayoutTab sourceLayoutTab = mLayoutTabs[0];
|
| + mLayoutTabs = new LayoutTab[] {sourceLayoutTab, newLayoutTab};
|
| + updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(id, sourceId)));
|
| +
|
| + forceAnimationToFinish();
|
| +
|
| + newLayoutTab.setBorderAlpha(0.0f);
|
| + final float scale = StackAnimation.SCALE_AMOUNT;
|
| + final float margin = Math.min(getWidth(), getHeight()) * (1.0f - scale) / 2.0f;
|
| +
|
| + // Step 1: zoom out the source tab and bring in the new tab
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.SCALE, 1.0f, scale,
|
| + BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRANSFORM_CURVE);
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.X, 0.0f, margin,
|
| + BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRANSFORM_CURVE);
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.Y, 0.0f, margin,
|
| + BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRANSFORM_CURVE);
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.BORDER_SCALE, 1.0f / scale, 1.0f,
|
| + BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRANSFORM_CURVE);
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.BORDER_ALPHA, 0.0f, 1.0f,
|
| + BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRANSFORM_CURVE);
|
| +
|
| + float pauseX = margin;
|
| + float pauseY = margin;
|
| + if (getOrientation() == Orientation.PORTRAIT) {
|
| + pauseY = BACKGROUND_COVER_PCTG * getHeight();
|
| + } else {
|
| + pauseX = BACKGROUND_COVER_PCTG * getWidth();
|
| + }
|
| +
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.ALPHA, 0.0f, 1.0f,
|
| + BACKGROUND_STEP1_DURATION / 2, 0, false, BakedBezierInterpolator.FADE_IN_CURVE);
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.SCALE, 0.f, scale,
|
| + BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.FADE_IN_CURVE);
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.X, originX, pauseX,
|
| + BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.FADE_IN_CURVE);
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.Y, originY, pauseY,
|
| + BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.FADE_IN_CURVE);
|
| +
|
| + // step 2: pause and admire the nice tabs
|
| +
|
| + // step 3: zoom in the source tab and slide down the new tab
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.SCALE, scale, 1.0f,
|
| + BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true,
|
| + BakedBezierInterpolator.TRANSFORM_CURVE);
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.X, margin, 0.0f,
|
| + BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true,
|
| + BakedBezierInterpolator.TRANSFORM_CURVE);
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.Y, margin, 0.0f,
|
| + BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true,
|
| + BakedBezierInterpolator.TRANSFORM_CURVE);
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.BORDER_SCALE, 1.0f, 1.0f / scale,
|
| + BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true,
|
| + BakedBezierInterpolator.TRANSFORM_CURVE);
|
| + addToAnimation(sourceLayoutTab, LayoutTab.Property.BORDER_ALPHA, 1.0f, 0.0f,
|
| + BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true,
|
| + BakedBezierInterpolator.TRANSFORM_CURVE);
|
| +
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.ALPHA, 1.f, 0.f, BACKGROUND_STEP3_DURATION,
|
| + BACKGROUND_STEP3_START, true, BakedBezierInterpolator.FADE_OUT_CURVE);
|
| + if (getOrientation() == Orientation.PORTRAIT) {
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.Y, pauseY, getHeight(),
|
| + BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true,
|
| + BakedBezierInterpolator.FADE_OUT_CURVE);
|
| + } else {
|
| + addToAnimation(newLayoutTab, LayoutTab.Property.X, pauseX, getWidth(),
|
| + BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true,
|
| + BakedBezierInterpolator.FADE_OUT_CURVE);
|
| + }
|
| +
|
| + mTabModelSelector.selectModel(newIsIncognito);
|
| + startHiding(sourceId, false);
|
| + }
|
| +
|
| + /**
|
| + * Set up for the tab closing animation
|
| + */
|
| + @Override
|
| + public void onTabClosing(long time, int id) {
|
| + reset();
|
| +
|
| + // Make sure any currently running animations can't influence tab if we are reusing it.
|
| + forceAnimationToFinish();
|
| +
|
| + // Create the {@link LayoutTab} for the tab before it is destroyed.
|
| + TabModel model = mTabModelSelector.getModelForTabId(id);
|
| + if (model != null) {
|
| + mClosedTab = createLayoutTab(id, model.isIncognito(), NO_CLOSE_BUTTON, NO_TITLE);
|
| + mClosedTab.setBorderAlpha(0.0f);
|
| + mLayoutTabs = new LayoutTab[] {mClosedTab};
|
| + updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(id)));
|
| + } else {
|
| + mLayoutTabs = null;
|
| + mClosedTab = null;
|
| + }
|
| + // Only close the id at the end when we are done querying the model.
|
| + super.onTabClosing(time, id);
|
| + }
|
| +
|
| + /**
|
| + * Animate the closing of a tab
|
| + */
|
| + @Override
|
| + public void onTabClosed(long time, int id, int nextId, boolean incognito) {
|
| + super.onTabClosed(time, id, nextId, incognito);
|
| +
|
| + if (mClosedTab != null) {
|
| + TabModel nextModel = mTabModelSelector.getModelForTabId(nextId);
|
| + if (nextModel != null) {
|
| + LayoutTab nextLayoutTab =
|
| + createLayoutTab(nextId, nextModel.isIncognito(), NO_CLOSE_BUTTON, NO_TITLE);
|
| + nextLayoutTab.setDrawDecoration(false);
|
| +
|
| + mLayoutTabs = new LayoutTab[] {nextLayoutTab, mClosedTab};
|
| + updateCacheVisibleIds(
|
| + new LinkedList<Integer>(Arrays.asList(nextId, mClosedTab.getId())));
|
| + } else {
|
| + mLayoutTabs = new LayoutTab[] {mClosedTab};
|
| + }
|
| +
|
| + forceAnimationToFinish();
|
| + mAnimatedTab = mClosedTab;
|
| + addToAnimation(this, Property.DISCARD_AMOUNT, 0, getDiscardRange(),
|
| + TAB_CLOSED_ANIMATION_DURATION, 0, false,
|
| + BakedBezierInterpolator.FADE_OUT_CURVE);
|
| +
|
| + mClosedTab = null;
|
| + if (nextModel != null) {
|
| + mTabModelSelector.selectModel(nextModel.isIncognito());
|
| + }
|
| + }
|
| + startHiding(nextId, false);
|
| + }
|
| +
|
| + /**
|
| + * Updates the position, scale, rotation and alpha values of mAnimatedTab.
|
| + *
|
| + * @param discard The value that specify how far along are we in the discard animation. 0 is
|
| + * filling the screen. Valid values are [-range .. range] where range is
|
| + * computed by {@link SimpleAnimationLayout#getDiscardRange()}.
|
| + */
|
| + private void setDiscardAmount(float discard) {
|
| + if (mAnimatedTab != null) {
|
| + final float range = getDiscardRange();
|
| + final float scale = Stack.computeDiscardScale(discard, range, true);
|
| +
|
| + final float deltaX = mAnimatedTab.getOriginalContentWidth();
|
| + final float deltaY = mAnimatedTab.getOriginalContentHeight() / 2.f;
|
| + mAnimatedTab.setX(deltaX * (1.f - scale));
|
| + mAnimatedTab.setY(deltaY * (1.f - scale));
|
| + mAnimatedTab.setScale(scale);
|
| + mAnimatedTab.setBorderScale(scale);
|
| + mAnimatedTab.setAlpha(Stack.computeDiscardAlpha(discard, range));
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @return The range of the discard amount.
|
| + */
|
| + private float getDiscardRange() {
|
| + return Math.min(getWidth(), getHeight()) * Stack.DISCARD_RANGE_SCREEN;
|
| + }
|
| +
|
| + @Override
|
| + public boolean onUpdateAnimation(long time, boolean jumpToEnd) {
|
| + return super.onUpdateAnimation(time, jumpToEnd) && mClosedTab == null;
|
| + }
|
| +
|
| + /**
|
| + * Resets the internal state.
|
| + */
|
| + private void reset() {
|
| + mLayoutTabs = null;
|
| + mAnimatedTab = null;
|
| + mClosedTab = null;
|
| + }
|
| +
|
| + /**
|
| + * Sets a property for an animation.
|
| + * @param prop The property to update
|
| + * @param value New value of the property
|
| + */
|
| + @Override
|
| + public void setProperty(Property prop, float value) {
|
| + switch (prop) {
|
| + case DISCARD_AMOUNT:
|
| + setDiscardAmount(value);
|
| + break;
|
| + default:
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + protected SceneLayer getSceneLayer() {
|
| + return mSceneLayer;
|
| + }
|
| +
|
| + @Override
|
| + protected void updateSceneLayer(Rect viewport, Rect contentViewport,
|
| + LayerTitleCache layerTitleCache, TabContentManager tabContentManager,
|
| + ResourceManager resourceManager, ChromeFullscreenManager fullscreenManager) {
|
| + super.updateSceneLayer(viewport, contentViewport, layerTitleCache, tabContentManager,
|
| + resourceManager, fullscreenManager);
|
| + assert mSceneLayer != null;
|
| + mSceneLayer.pushLayers(getContext(), viewport, contentViewport, this, layerTitleCache,
|
| + tabContentManager, resourceManager);
|
| + }
|
| +}
|
|
|