OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.compositor.layouts.phone; |
| 6 |
| 7 import android.content.Context; |
| 8 import android.graphics.Rect; |
| 9 import android.view.animation.Interpolator; |
| 10 |
| 11 import org.chromium.chrome.browser.compositor.LayerTitleCache; |
| 12 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable
; |
| 13 import org.chromium.chrome.browser.compositor.layouts.Layout; |
| 14 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost; |
| 15 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; |
| 16 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab; |
| 17 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; |
| 18 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter; |
| 19 import org.chromium.chrome.browser.compositor.layouts.phone.stack.Stack; |
| 20 import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation
; |
| 21 import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer; |
| 22 import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer; |
| 23 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; |
| 24 import org.chromium.chrome.browser.tabmodel.TabModel; |
| 25 import org.chromium.ui.interpolators.BakedBezierInterpolator; |
| 26 import org.chromium.ui.resources.ResourceManager; |
| 27 |
| 28 import java.util.Arrays; |
| 29 import java.util.LinkedList; |
| 30 |
| 31 /** |
| 32 * This class handles animating the opening of new tabs. |
| 33 */ |
| 34 public class SimpleAnimationLayout |
| 35 extends Layout implements Animatable<SimpleAnimationLayout.Property> { |
| 36 /** |
| 37 * Animation properties |
| 38 */ |
| 39 public enum Property { DISCARD_AMOUNT } |
| 40 |
| 41 /** Duration of the first step of the background animation: zooming out, rot
ating in */ |
| 42 private static final long BACKGROUND_STEP1_DURATION = 300; |
| 43 /** Duration of the second step of the background animation: pause */ |
| 44 private static final long BACKGROUND_STEP2_DURATION = 150; |
| 45 /** Duration of the third step of the background animation: zooming in, slid
ing out */ |
| 46 private static final long BACKGROUND_STEP3_DURATION = 300; |
| 47 /** Start time offset of the third step of the background animation */ |
| 48 private static final long BACKGROUND_STEP3_START = |
| 49 BACKGROUND_STEP1_DURATION + BACKGROUND_STEP2_DURATION; |
| 50 /** Percentage of the screen covered by the new tab */ |
| 51 private static final float BACKGROUND_COVER_PCTG = 0.5f; |
| 52 |
| 53 /** The time duration of the animation */ |
| 54 protected static final int FOREGROUND_ANIMATION_DURATION = 300; |
| 55 |
| 56 /** The time duration of the animation */ |
| 57 protected static final int TAB_CLOSED_ANIMATION_DURATION = 250; |
| 58 |
| 59 /** |
| 60 * A cached {@link LayoutTab} representation of the currently closing tab. I
f it's not |
| 61 * null, it means tabClosing() has been called to start animation setup but |
| 62 * tabClosed() has not yet been called to finish animation startup |
| 63 */ |
| 64 private LayoutTab mClosedTab; |
| 65 |
| 66 private LayoutTab mAnimatedTab; |
| 67 private final TabListSceneLayer mSceneLayer; |
| 68 |
| 69 /** |
| 70 * Creates an instance of the {@link SimpleAnimationLayout}. |
| 71 * @param context The current Android's context. |
| 72 * @param updateHost The {@link LayoutUpdateHost} view for this layout. |
| 73 * @param renderHost The {@link LayoutRenderHost} view for this layout. |
| 74 * @param eventFilter The {@link EventFilter} that is needed for this view. |
| 75 */ |
| 76 public SimpleAnimationLayout(Context context, LayoutUpdateHost updateHost, |
| 77 LayoutRenderHost renderHost, EventFilter eventFilter) { |
| 78 super(context, updateHost, renderHost, eventFilter); |
| 79 mSceneLayer = new TabListSceneLayer(); |
| 80 } |
| 81 |
| 82 @Override |
| 83 public int getSizingFlags() { |
| 84 return SizingFlags.HELPER_SUPPORTS_FULLSCREEN; |
| 85 } |
| 86 |
| 87 @Override |
| 88 public void show(long time, boolean animate) { |
| 89 super.show(time, animate); |
| 90 reset(); |
| 91 } |
| 92 |
| 93 @Override |
| 94 public boolean handlesTabCreating() { |
| 95 return true; |
| 96 } |
| 97 |
| 98 @Override |
| 99 public boolean handlesTabClosing() { |
| 100 return true; |
| 101 } |
| 102 |
| 103 @Override |
| 104 protected void updateLayout(long time, long dt) { |
| 105 super.updateLayout(time, dt); |
| 106 if (mLayoutTabs == null) return; |
| 107 boolean needUpdate = false; |
| 108 for (int i = mLayoutTabs.length - 1; i >= 0; i--) { |
| 109 needUpdate = mLayoutTabs[i].updateSnap(dt) || needUpdate; |
| 110 } |
| 111 if (needUpdate) requestUpdate(); |
| 112 } |
| 113 |
| 114 @Override |
| 115 public void onTabCreating(int sourceTabId) { |
| 116 super.onTabCreating(sourceTabId); |
| 117 reset(); |
| 118 |
| 119 // Make sure any currently running animations can't influence tab if we
are reusing it. |
| 120 forceAnimationToFinish(); |
| 121 |
| 122 ensureSourceTabCreated(sourceTabId); |
| 123 } |
| 124 |
| 125 private void ensureSourceTabCreated(int sourceTabId) { |
| 126 if (mLayoutTabs != null && mLayoutTabs.length == 1 |
| 127 && mLayoutTabs[0].getId() == sourceTabId) { |
| 128 return; |
| 129 } |
| 130 // Just draw the source tab on the screen. |
| 131 TabModel sourceModel = mTabModelSelector.getModelForTabId(sourceTabId); |
| 132 if (sourceModel == null) return; |
| 133 LayoutTab sourceLayoutTab = |
| 134 createLayoutTab(sourceTabId, sourceModel.isIncognito(), NO_CLOSE
_BUTTON, NO_TITLE); |
| 135 sourceLayoutTab.setBorderAlpha(0.0f); |
| 136 |
| 137 mLayoutTabs = new LayoutTab[] {sourceLayoutTab}; |
| 138 updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(sourceTabId)
)); |
| 139 } |
| 140 |
| 141 @Override |
| 142 public void onTabCreated(long time, int id, int index, int sourceId, boolean
newIsIncognito, |
| 143 boolean background, float originX, float originY) { |
| 144 super.onTabCreated(time, id, index, sourceId, newIsIncognito, background
, originX, originY); |
| 145 ensureSourceTabCreated(sourceId); |
| 146 if (background && mLayoutTabs != null && mLayoutTabs.length > 0) { |
| 147 tabCreatedInBackground(id, sourceId, newIsIncognito, originX, origin
Y); |
| 148 } else { |
| 149 tabCreatedInForeground(id, sourceId, newIsIncognito, originX, origin
Y); |
| 150 } |
| 151 } |
| 152 |
| 153 /** |
| 154 * Animate opening a tab in the foreground. |
| 155 * |
| 156 * @param id The id of the new tab to animate. |
| 157 * @param sourceId The id of the tab that spawned this new tab. |
| 158 * @param newIsIncognito true if the new tab is an incognito tab. |
| 159 * @param originX The X coordinate of the last touch down event that
spawned this tab. |
| 160 * @param originY The Y coordinate of the last touch down event that
spawned this tab. |
| 161 */ |
| 162 private void tabCreatedInForeground( |
| 163 int id, int sourceId, boolean newIsIncognito, float originX, float o
riginY) { |
| 164 LayoutTab newLayoutTab = createLayoutTab(id, newIsIncognito, NO_CLOSE_BU
TTON, NO_TITLE); |
| 165 if (mLayoutTabs == null || mLayoutTabs.length == 0) { |
| 166 mLayoutTabs = new LayoutTab[] {newLayoutTab}; |
| 167 } else { |
| 168 mLayoutTabs = new LayoutTab[] {mLayoutTabs[0], newLayoutTab}; |
| 169 } |
| 170 updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(id, sourceId
))); |
| 171 |
| 172 newLayoutTab.setBorderAlpha(0.0f); |
| 173 newLayoutTab.setStaticToViewBlend(1.f); |
| 174 |
| 175 forceAnimationToFinish(); |
| 176 |
| 177 Interpolator interpolator = BakedBezierInterpolator.TRANSFORM_CURVE; |
| 178 addToAnimation(newLayoutTab, LayoutTab.Property.SCALE, 0.f, 1.f, |
| 179 FOREGROUND_ANIMATION_DURATION, 0, false, interpolator); |
| 180 addToAnimation(newLayoutTab, LayoutTab.Property.ALPHA, 0.f, 1.f, |
| 181 FOREGROUND_ANIMATION_DURATION, 0, false, interpolator); |
| 182 addToAnimation(newLayoutTab, LayoutTab.Property.X, originX, 0.f, |
| 183 FOREGROUND_ANIMATION_DURATION, 0, false, interpolator); |
| 184 addToAnimation(newLayoutTab, LayoutTab.Property.Y, originY, 0.f, |
| 185 FOREGROUND_ANIMATION_DURATION, 0, false, interpolator); |
| 186 |
| 187 mTabModelSelector.selectModel(newIsIncognito); |
| 188 startHiding(id, false); |
| 189 } |
| 190 |
| 191 /** |
| 192 * Animate opening a tab in the background. |
| 193 * |
| 194 * @param id The id of the new tab to animate. |
| 195 * @param sourceId The id of the tab that spawned this new tab. |
| 196 * @param newIsIncognito true if the new tab is an incognito tab. |
| 197 * @param originX The X screen coordinate in dp of the last touch dow
n event that spawned |
| 198 * this tab. |
| 199 * @param originY The Y screen coordinate in dp of the last touch dow
n event that spawned |
| 200 * this tab. |
| 201 */ |
| 202 private void tabCreatedInBackground( |
| 203 int id, int sourceId, boolean newIsIncognito, float originX, float o
riginY) { |
| 204 LayoutTab newLayoutTab = createLayoutTab(id, newIsIncognito, NO_CLOSE_BU
TTON, NEED_TITLE); |
| 205 // mLayoutTabs should already have the source tab from tabCreating(). |
| 206 assert mLayoutTabs.length == 1; |
| 207 LayoutTab sourceLayoutTab = mLayoutTabs[0]; |
| 208 mLayoutTabs = new LayoutTab[] {sourceLayoutTab, newLayoutTab}; |
| 209 updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(id, sourceId
))); |
| 210 |
| 211 forceAnimationToFinish(); |
| 212 |
| 213 newLayoutTab.setBorderAlpha(0.0f); |
| 214 final float scale = StackAnimation.SCALE_AMOUNT; |
| 215 final float margin = Math.min(getWidth(), getHeight()) * (1.0f - scale)
/ 2.0f; |
| 216 |
| 217 // Step 1: zoom out the source tab and bring in the new tab |
| 218 addToAnimation(sourceLayoutTab, LayoutTab.Property.SCALE, 1.0f, scale, |
| 219 BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRA
NSFORM_CURVE); |
| 220 addToAnimation(sourceLayoutTab, LayoutTab.Property.X, 0.0f, margin, |
| 221 BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRA
NSFORM_CURVE); |
| 222 addToAnimation(sourceLayoutTab, LayoutTab.Property.Y, 0.0f, margin, |
| 223 BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRA
NSFORM_CURVE); |
| 224 addToAnimation(sourceLayoutTab, LayoutTab.Property.BORDER_SCALE, 1.0f /
scale, 1.0f, |
| 225 BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRA
NSFORM_CURVE); |
| 226 addToAnimation(sourceLayoutTab, LayoutTab.Property.BORDER_ALPHA, 0.0f, 1
.0f, |
| 227 BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.TRA
NSFORM_CURVE); |
| 228 |
| 229 float pauseX = margin; |
| 230 float pauseY = margin; |
| 231 if (getOrientation() == Orientation.PORTRAIT) { |
| 232 pauseY = BACKGROUND_COVER_PCTG * getHeight(); |
| 233 } else { |
| 234 pauseX = BACKGROUND_COVER_PCTG * getWidth(); |
| 235 } |
| 236 |
| 237 addToAnimation(newLayoutTab, LayoutTab.Property.ALPHA, 0.0f, 1.0f, |
| 238 BACKGROUND_STEP1_DURATION / 2, 0, false, BakedBezierInterpolator
.FADE_IN_CURVE); |
| 239 addToAnimation(newLayoutTab, LayoutTab.Property.SCALE, 0.f, scale, |
| 240 BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.FAD
E_IN_CURVE); |
| 241 addToAnimation(newLayoutTab, LayoutTab.Property.X, originX, pauseX, |
| 242 BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.FAD
E_IN_CURVE); |
| 243 addToAnimation(newLayoutTab, LayoutTab.Property.Y, originY, pauseY, |
| 244 BACKGROUND_STEP1_DURATION, 0, false, BakedBezierInterpolator.FAD
E_IN_CURVE); |
| 245 |
| 246 // step 2: pause and admire the nice tabs |
| 247 |
| 248 // step 3: zoom in the source tab and slide down the new tab |
| 249 addToAnimation(sourceLayoutTab, LayoutTab.Property.SCALE, scale, 1.0f, |
| 250 BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true, |
| 251 BakedBezierInterpolator.TRANSFORM_CURVE); |
| 252 addToAnimation(sourceLayoutTab, LayoutTab.Property.X, margin, 0.0f, |
| 253 BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true, |
| 254 BakedBezierInterpolator.TRANSFORM_CURVE); |
| 255 addToAnimation(sourceLayoutTab, LayoutTab.Property.Y, margin, 0.0f, |
| 256 BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true, |
| 257 BakedBezierInterpolator.TRANSFORM_CURVE); |
| 258 addToAnimation(sourceLayoutTab, LayoutTab.Property.BORDER_SCALE, 1.0f, 1
.0f / scale, |
| 259 BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true, |
| 260 BakedBezierInterpolator.TRANSFORM_CURVE); |
| 261 addToAnimation(sourceLayoutTab, LayoutTab.Property.BORDER_ALPHA, 1.0f, 0
.0f, |
| 262 BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true, |
| 263 BakedBezierInterpolator.TRANSFORM_CURVE); |
| 264 |
| 265 addToAnimation(newLayoutTab, LayoutTab.Property.ALPHA, 1.f, 0.f, BACKGRO
UND_STEP3_DURATION, |
| 266 BACKGROUND_STEP3_START, true, BakedBezierInterpolator.FADE_OUT_C
URVE); |
| 267 if (getOrientation() == Orientation.PORTRAIT) { |
| 268 addToAnimation(newLayoutTab, LayoutTab.Property.Y, pauseY, getHeight
(), |
| 269 BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true, |
| 270 BakedBezierInterpolator.FADE_OUT_CURVE); |
| 271 } else { |
| 272 addToAnimation(newLayoutTab, LayoutTab.Property.X, pauseX, getWidth(
), |
| 273 BACKGROUND_STEP3_DURATION, BACKGROUND_STEP3_START, true, |
| 274 BakedBezierInterpolator.FADE_OUT_CURVE); |
| 275 } |
| 276 |
| 277 mTabModelSelector.selectModel(newIsIncognito); |
| 278 startHiding(sourceId, false); |
| 279 } |
| 280 |
| 281 /** |
| 282 * Set up for the tab closing animation |
| 283 */ |
| 284 @Override |
| 285 public void onTabClosing(long time, int id) { |
| 286 reset(); |
| 287 |
| 288 // Make sure any currently running animations can't influence tab if we
are reusing it. |
| 289 forceAnimationToFinish(); |
| 290 |
| 291 // Create the {@link LayoutTab} for the tab before it is destroyed. |
| 292 TabModel model = mTabModelSelector.getModelForTabId(id); |
| 293 if (model != null) { |
| 294 mClosedTab = createLayoutTab(id, model.isIncognito(), NO_CLOSE_BUTTO
N, NO_TITLE); |
| 295 mClosedTab.setBorderAlpha(0.0f); |
| 296 mLayoutTabs = new LayoutTab[] {mClosedTab}; |
| 297 updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(id))); |
| 298 } else { |
| 299 mLayoutTabs = null; |
| 300 mClosedTab = null; |
| 301 } |
| 302 // Only close the id at the end when we are done querying the model. |
| 303 super.onTabClosing(time, id); |
| 304 } |
| 305 |
| 306 /** |
| 307 * Animate the closing of a tab |
| 308 */ |
| 309 @Override |
| 310 public void onTabClosed(long time, int id, int nextId, boolean incognito) { |
| 311 super.onTabClosed(time, id, nextId, incognito); |
| 312 |
| 313 if (mClosedTab != null) { |
| 314 TabModel nextModel = mTabModelSelector.getModelForTabId(nextId); |
| 315 if (nextModel != null) { |
| 316 LayoutTab nextLayoutTab = |
| 317 createLayoutTab(nextId, nextModel.isIncognito(), NO_CLOS
E_BUTTON, NO_TITLE); |
| 318 nextLayoutTab.setDrawDecoration(false); |
| 319 |
| 320 mLayoutTabs = new LayoutTab[] {nextLayoutTab, mClosedTab}; |
| 321 updateCacheVisibleIds( |
| 322 new LinkedList<Integer>(Arrays.asList(nextId, mClosedTab
.getId()))); |
| 323 } else { |
| 324 mLayoutTabs = new LayoutTab[] {mClosedTab}; |
| 325 } |
| 326 |
| 327 forceAnimationToFinish(); |
| 328 mAnimatedTab = mClosedTab; |
| 329 addToAnimation(this, Property.DISCARD_AMOUNT, 0, getDiscardRange(), |
| 330 TAB_CLOSED_ANIMATION_DURATION, 0, false, |
| 331 BakedBezierInterpolator.FADE_OUT_CURVE); |
| 332 |
| 333 mClosedTab = null; |
| 334 if (nextModel != null) { |
| 335 mTabModelSelector.selectModel(nextModel.isIncognito()); |
| 336 } |
| 337 } |
| 338 startHiding(nextId, false); |
| 339 } |
| 340 |
| 341 /** |
| 342 * Updates the position, scale, rotation and alpha values of mAnimatedTab. |
| 343 * |
| 344 * @param discard The value that specify how far along are we in the discard
animation. 0 is |
| 345 * filling the screen. Valid values are [-range .. range] whe
re range is |
| 346 * computed by {@link SimpleAnimationLayout#getDiscardRange()
}. |
| 347 */ |
| 348 private void setDiscardAmount(float discard) { |
| 349 if (mAnimatedTab != null) { |
| 350 final float range = getDiscardRange(); |
| 351 final float scale = Stack.computeDiscardScale(discard, range, true); |
| 352 |
| 353 final float deltaX = mAnimatedTab.getOriginalContentWidth(); |
| 354 final float deltaY = mAnimatedTab.getOriginalContentHeight() / 2.f; |
| 355 mAnimatedTab.setX(deltaX * (1.f - scale)); |
| 356 mAnimatedTab.setY(deltaY * (1.f - scale)); |
| 357 mAnimatedTab.setScale(scale); |
| 358 mAnimatedTab.setBorderScale(scale); |
| 359 mAnimatedTab.setAlpha(Stack.computeDiscardAlpha(discard, range)); |
| 360 } |
| 361 } |
| 362 |
| 363 /** |
| 364 * @return The range of the discard amount. |
| 365 */ |
| 366 private float getDiscardRange() { |
| 367 return Math.min(getWidth(), getHeight()) * Stack.DISCARD_RANGE_SCREEN; |
| 368 } |
| 369 |
| 370 @Override |
| 371 public boolean onUpdateAnimation(long time, boolean jumpToEnd) { |
| 372 return super.onUpdateAnimation(time, jumpToEnd) && mClosedTab == null; |
| 373 } |
| 374 |
| 375 /** |
| 376 * Resets the internal state. |
| 377 */ |
| 378 private void reset() { |
| 379 mLayoutTabs = null; |
| 380 mAnimatedTab = null; |
| 381 mClosedTab = null; |
| 382 } |
| 383 |
| 384 /** |
| 385 * Sets a property for an animation. |
| 386 * @param prop The property to update |
| 387 * @param value New value of the property |
| 388 */ |
| 389 @Override |
| 390 public void setProperty(Property prop, float value) { |
| 391 switch (prop) { |
| 392 case DISCARD_AMOUNT: |
| 393 setDiscardAmount(value); |
| 394 break; |
| 395 default: |
| 396 } |
| 397 } |
| 398 |
| 399 @Override |
| 400 protected SceneLayer getSceneLayer() { |
| 401 return mSceneLayer; |
| 402 } |
| 403 |
| 404 @Override |
| 405 protected void updateSceneLayer(Rect viewport, Rect contentViewport, |
| 406 LayerTitleCache layerTitleCache, TabContentManager tabContentManager
, |
| 407 ResourceManager resourceManager, ChromeFullscreenManager fullscreenM
anager) { |
| 408 super.updateSceneLayer(viewport, contentViewport, layerTitleCache, tabCo
ntentManager, |
| 409 resourceManager, fullscreenManager); |
| 410 assert mSceneLayer != null; |
| 411 mSceneLayer.pushLayers(getContext(), viewport, contentViewport, this, la
yerTitleCache, |
| 412 tabContentManager, resourceManager); |
| 413 } |
| 414 } |
OLD | NEW |