LauncherStateTransitionAnimation.java revision 2a53cb7a5e86920fdc9063ddf3966af5d5472c56
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.launcher3; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.AnimatorSet; 22import android.animation.ObjectAnimator; 23import android.animation.PropertyValuesHolder; 24import android.animation.TimeInterpolator; 25import android.content.res.Resources; 26import android.util.Log; 27import android.view.View; 28import android.view.ViewAnimationUtils; 29import android.view.animation.AccelerateInterpolator; 30import android.view.animation.DecelerateInterpolator; 31 32import com.android.launcher3.util.Thunk; 33import com.android.launcher3.widget.WidgetsContainerView; 34 35import java.util.HashMap; 36 37/** 38 * TODO: figure out what kind of tests we can write for this 39 * 40 * Things to test when changing the following class. 41 * - Home from workspace 42 * - from center screen 43 * - from other screens 44 * - Home from all apps 45 * - from center screen 46 * - from other screens 47 * - Back from all apps 48 * - from center screen 49 * - from other screens 50 * - Launch app from workspace and quit 51 * - with back 52 * - with home 53 * - Launch app from all apps and quit 54 * - with back 55 * - with home 56 * - Go to a screen that's not the default, then all 57 * apps, and launch and app, and go back 58 * - with back 59 * -with home 60 * - On workspace, long press power and go back 61 * - with back 62 * - with home 63 * - On all apps, long press power and go back 64 * - with back 65 * - with home 66 * - On workspace, power off 67 * - On all apps, power off 68 * - Launch an app and turn off the screen while in that app 69 * - Go back with home key 70 * - Go back with back key TODO: make this not go to workspace 71 * - From all apps 72 * - From workspace 73 * - Enter and exit car mode (becuase it causes an extra configuration changed) 74 * - From all apps 75 * - From the center workspace 76 * - From another workspace 77 */ 78public class LauncherStateTransitionAnimation { 79 80 /** 81 * Callbacks made during the state transition 82 */ 83 interface Callbacks { 84 public void onStateTransitionHideSearchBar(); 85 } 86 87 /** 88 * Private callbacks made during transition setup. 89 */ 90 static abstract class PrivateTransitionCallbacks { 91 void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {} 92 void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {} 93 float getMaterialRevealViewFinalAlpha(View revealView) { 94 return 0; 95 } 96 float getMaterialRevealViewFinalXDrift(View revealView) { 97 return 0; 98 } 99 float getMaterialRevealViewFinalYDrift(View revealView) { 100 return 0; 101 } 102 float getMaterialRevealViewStartFinalRadius() { 103 return 0; 104 } 105 AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView, 106 View allAppsButtonView) { 107 return null; 108 } 109 } 110 111 public static final String TAG = "LauncherStateTransitionAnimation"; 112 113 // Flags to determine how to set the layers on views before the transition animation 114 public static final int BUILD_LAYER = 0; 115 public static final int BUILD_AND_SET_LAYER = 1; 116 public static final int SINGLE_FRAME_DELAY = 16; 117 118 @Thunk Launcher mLauncher; 119 @Thunk Callbacks mCb; 120 @Thunk AnimatorSet mStateAnimation; 121 122 public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) { 123 mLauncher = l; 124 mCb = cb; 125 } 126 127 /** 128 * Starts an animation to the apps view. 129 */ 130 public void startAnimationToAllApps(final boolean animated) { 131 final AppsContainerView toView = mLauncher.getAppsView(); 132 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { 133 private int[] mAllAppsToPanelDelta; 134 135 @Override 136 public void onRevealViewVisible(View revealView, View contentView, 137 View allAppsButtonView) { 138 toView.setBackground(null); 139 // Get the y delta between the center of the page and the center of the all apps 140 // button 141 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, 142 allAppsButtonView, null); 143 } 144 @Override 145 public float getMaterialRevealViewFinalAlpha(View revealView) { 146 return 1f; 147 } 148 @Override 149 public float getMaterialRevealViewFinalXDrift(View revealView) { 150 return mAllAppsToPanelDelta[0]; 151 } 152 @Override 153 public float getMaterialRevealViewFinalYDrift(View revealView) { 154 return mAllAppsToPanelDelta[1]; 155 } 156 @Override 157 public float getMaterialRevealViewStartFinalRadius() { 158 int allAppsButtonSize = LauncherAppState.getInstance(). 159 getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; 160 return allAppsButtonSize / 2; 161 } 162 @Override 163 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( 164 final View revealView, final View allAppsButtonView) { 165 return new AnimatorListenerAdapter() { 166 public void onAnimationStart(Animator animation) { 167 allAppsButtonView.setVisibility(View.INVISIBLE); 168 } 169 public void onAnimationEnd(Animator animation) { 170 allAppsButtonView.setVisibility(View.VISIBLE); 171 } 172 }; 173 } 174 }; 175 startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(), 176 toView.getRevealView(), animated, 177 !mLauncher.isAllAppsSearchOverridden() /* hideSearchBar */, cb); 178 } 179 180 /** 181 * Starts an animation to the widgets view. 182 */ 183 public void startAnimationToWidgets(final boolean animated) { 184 final WidgetsContainerView toView = mLauncher.getWidgetsView(); 185 final Resources res = mLauncher.getResources(); 186 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { 187 @Override 188 public void onRevealViewVisible(View revealView, View contentView, 189 View allAppsButtonView) { 190 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); 191 } 192 @Override 193 public float getMaterialRevealViewFinalAlpha(View revealView) { 194 return 0.3f; 195 } 196 @Override 197 public float getMaterialRevealViewFinalYDrift(View revealView) { 198 return revealView.getMeasuredHeight() / 2; 199 } 200 }; 201 startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, 202 toView.getContentView(), toView.getRevealView(), animated, true /* hideSearchBar */, 203 cb); 204 } 205 206 /** 207 * Starts and animation to the workspace from the current overlay view. 208 */ 209 public void startAnimationToWorkspace(final Launcher.State fromState, 210 final Workspace.State toWorkspaceState, final int toWorkspacePage, 211 final boolean animated, final Runnable onCompleteRunnable) { 212 if (toWorkspaceState != Workspace.State.NORMAL && 213 toWorkspaceState != Workspace.State.SPRING_LOADED && 214 toWorkspaceState != Workspace.State.OVERVIEW) { 215 Log.e(TAG, "Unexpected call to startAnimationToWorkspace"); 216 } 217 218 if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { 219 startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, toWorkspacePage, 220 animated, onCompleteRunnable); 221 } else { 222 startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, toWorkspacePage, 223 animated, onCompleteRunnable); 224 } 225 } 226 227 /** 228 * Creates and starts a new animation to a particular overlay view. 229 */ 230 private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView, 231 final View contentView, final View revealView, final boolean animated, 232 final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) { 233 final Resources res = mLauncher.getResources(); 234 final boolean material = Utilities.isLmpOrAbove(); 235 final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); 236 final int itemsAlphaStagger = 237 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); 238 239 final View allAppsButtonView = mLauncher.getAllAppsButton(); 240 final View fromView = mLauncher.getWorkspace(); 241 242 final HashMap<View, Integer> layerViews = new HashMap<>(); 243 244 // If for some reason our views aren't initialized, don't animate 245 boolean initialized = allAppsButtonView != null; 246 247 // Cancel the current animation 248 cancelAnimation(); 249 250 // Create the workspace animation. 251 // NOTE: this call apparently also sets the state for the workspace if !animated 252 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1, 253 animated, layerViews); 254 255 if (animated && initialized) { 256 mStateAnimation = LauncherAnimUtils.createAnimatorSet(); 257 258 // Setup the reveal view animation 259 int width = revealView.getMeasuredWidth(); 260 int height = revealView.getMeasuredHeight(); 261 float revealRadius = (float) Math.hypot(width / 2, height / 2); 262 revealView.setVisibility(View.VISIBLE); 263 revealView.setAlpha(0f); 264 revealView.setTranslationY(0f); 265 revealView.setTranslationX(0f); 266 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView); 267 268 // Calculate the final animation values 269 final float revealViewToAlpha; 270 final float revealViewToXDrift; 271 final float revealViewToYDrift; 272 if (material) { 273 revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView); 274 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView); 275 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView); 276 } else { 277 revealViewToAlpha = 0f; 278 revealViewToYDrift = 2 * height / 3; 279 revealViewToXDrift = 0; 280 } 281 282 // Create the animators 283 PropertyValuesHolder panelAlpha = 284 PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f); 285 PropertyValuesHolder panelDriftY = 286 PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0); 287 PropertyValuesHolder panelDriftX = 288 PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0); 289 ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView, 290 panelAlpha, panelDriftY, panelDriftX); 291 panelAlphaAndDrift.setDuration(revealDuration); 292 panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); 293 294 // Play the animation 295 layerViews.put(revealView, BUILD_AND_SET_LAYER); 296 mStateAnimation.play(panelAlphaAndDrift); 297 298 // Setup the animation for the content view 299 contentView.setVisibility(View.VISIBLE); 300 contentView.setAlpha(0f); 301 contentView.setTranslationY(revealViewToYDrift); 302 layerViews.put(contentView, BUILD_AND_SET_LAYER); 303 304 // Create the individual animators 305 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY", 306 revealViewToYDrift, 0); 307 pageDrift.setDuration(revealDuration); 308 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); 309 pageDrift.setStartDelay(itemsAlphaStagger); 310 mStateAnimation.play(pageDrift); 311 312 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f); 313 itemsAlpha.setDuration(revealDuration); 314 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); 315 itemsAlpha.setStartDelay(itemsAlphaStagger); 316 mStateAnimation.play(itemsAlpha); 317 318 if (material) { 319 // Animate the all apps button 320 float startRadius = pCb.getMaterialRevealViewStartFinalRadius(); 321 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener( 322 revealView, allAppsButtonView); 323 Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2, 324 height / 2, startRadius, revealRadius); 325 reveal.setDuration(revealDuration); 326 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); 327 if (listener != null) { 328 reveal.addListener(listener); 329 } 330 mStateAnimation.play(reveal); 331 } 332 333 mStateAnimation.addListener(new AnimatorListenerAdapter() { 334 @Override 335 public void onAnimationEnd(Animator animation) { 336 dispatchOnLauncherTransitionEnd(fromView, animated, false); 337 dispatchOnLauncherTransitionEnd(toView, animated, false); 338 339 // Hide the reveal view 340 revealView.setVisibility(View.INVISIBLE); 341 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView); 342 343 // Disable all necessary layers 344 for (View v : layerViews.keySet()) { 345 if (layerViews.get(v) == BUILD_AND_SET_LAYER) { 346 v.setLayerType(View.LAYER_TYPE_NONE, null); 347 } 348 } 349 350 if (hideSearchBar) { 351 mCb.onStateTransitionHideSearchBar(); 352 } 353 354 // This can hold unnecessary references to views. 355 mStateAnimation = null; 356 } 357 358 }); 359 360 // Play the workspace animation 361 if (workspaceAnim != null) { 362 mStateAnimation.play(workspaceAnim); 363 } 364 365 // Dispatch the prepare transition signal 366 dispatchOnLauncherTransitionPrepare(fromView, animated, false); 367 dispatchOnLauncherTransitionPrepare(toView, animated, false); 368 369 370 final AnimatorSet stateAnimation = mStateAnimation; 371 final Runnable startAnimRunnable = new Runnable() { 372 public void run() { 373 // Check that mStateAnimation hasn't changed while 374 // we waited for a layout/draw pass 375 if (mStateAnimation != stateAnimation) 376 return; 377 dispatchOnLauncherTransitionStart(fromView, animated, false); 378 dispatchOnLauncherTransitionStart(toView, animated, false); 379 380 // Enable all necessary layers 381 for (View v : layerViews.keySet()) { 382 if (layerViews.get(v) == BUILD_AND_SET_LAYER) { 383 v.setLayerType(View.LAYER_TYPE_HARDWARE, null); 384 } 385 if (Utilities.isViewAttachedToWindow(v)) { 386 v.buildLayer(); 387 } 388 } 389 390 // Focus the new view 391 toView.requestFocus(); 392 393 mStateAnimation.start(); 394 } 395 }; 396 toView.bringToFront(); 397 toView.setVisibility(View.VISIBLE); 398 toView.post(startAnimRunnable); 399 } else { 400 toView.setTranslationX(0.0f); 401 toView.setTranslationY(0.0f); 402 toView.setScaleX(1.0f); 403 toView.setScaleY(1.0f); 404 toView.setVisibility(View.VISIBLE); 405 toView.bringToFront(); 406 407 // Show the content view 408 contentView.setVisibility(View.VISIBLE); 409 410 if (hideSearchBar) { 411 mCb.onStateTransitionHideSearchBar(); 412 } 413 414 dispatchOnLauncherTransitionPrepare(fromView, animated, false); 415 dispatchOnLauncherTransitionStart(fromView, animated, false); 416 dispatchOnLauncherTransitionEnd(fromView, animated, false); 417 dispatchOnLauncherTransitionPrepare(toView, animated, false); 418 dispatchOnLauncherTransitionStart(toView, animated, false); 419 dispatchOnLauncherTransitionEnd(toView, animated, false); 420 } 421 } 422 423 /** 424 * Starts and animation to the workspace from the apps view. 425 */ 426 private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState, 427 final Workspace.State toWorkspaceState, final int toWorkspacePage, 428 final boolean animated, final Runnable onCompleteRunnable) { 429 AppsContainerView appsView = mLauncher.getAppsView(); 430 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { 431 int[] mAllAppsToPanelDelta; 432 433 @Override 434 public void onRevealViewVisible(View revealView, View contentView, 435 View allAppsButtonView) { 436 // Get the y delta between the center of the page and the center of the all apps 437 // button 438 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, 439 allAppsButtonView, null); 440 } 441 @Override 442 public float getMaterialRevealViewFinalXDrift(View revealView) { 443 return mAllAppsToPanelDelta[0]; 444 } 445 @Override 446 public float getMaterialRevealViewFinalYDrift(View revealView) { 447 return mAllAppsToPanelDelta[1]; 448 } 449 @Override 450 float getMaterialRevealViewFinalAlpha(View revealView) { 451 // No alpha anim from all apps 452 return 1f; 453 } 454 @Override 455 float getMaterialRevealViewStartFinalRadius() { 456 int allAppsButtonSize = LauncherAppState.getInstance(). 457 getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; 458 return allAppsButtonSize / 2; 459 } 460 @Override 461 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( 462 final View revealView, final View allAppsButtonView) { 463 return new AnimatorListenerAdapter() { 464 public void onAnimationStart(Animator animation) { 465 // We set the alpha instead of visibility to ensure that the focus does not 466 // get taken from the all apps view 467 allAppsButtonView.setVisibility(View.VISIBLE); 468 allAppsButtonView.setAlpha(0f); 469 } 470 public void onAnimationEnd(Animator animation) { 471 // Hide the reveal view 472 revealView.setVisibility(View.INVISIBLE); 473 474 // Show the all apps button, and focus it 475 allAppsButtonView.setAlpha(1f); 476 } 477 }; 478 } 479 }; 480 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView, 481 appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable, 482 cb); 483 } 484 485 /** 486 * Starts and animation to the workspace from the widgets view. 487 */ 488 private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, 489 final Workspace.State toWorkspaceState, final int toWorkspacePage, 490 final boolean animated, final Runnable onCompleteRunnable) { 491 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); 492 final Resources res = mLauncher.getResources(); 493 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { 494 @Override 495 public void onRevealViewVisible(View revealView, View contentView, 496 View allAppsButtonView) { 497 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); 498 } 499 @Override 500 public float getMaterialRevealViewFinalYDrift(View revealView) { 501 return revealView.getMeasuredHeight() / 2; 502 } 503 @Override 504 float getMaterialRevealViewFinalAlpha(View revealView) { 505 return 0.4f; 506 } 507 @Override 508 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( 509 final View revealView, final View allAppsButtonView) { 510 return new AnimatorListenerAdapter() { 511 public void onAnimationEnd(Animator animation) { 512 // Hide the reveal view 513 revealView.setVisibility(View.INVISIBLE); 514 } 515 }; 516 } 517 }; 518 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView, 519 widgetsView.getContentView(), widgetsView.getRevealView(), animated, 520 onCompleteRunnable, cb); 521 } 522 523 /** 524 * Creates and starts a new animation to the workspace. 525 */ 526 private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, 527 final int toWorkspacePage, final View fromView, final View contentView, 528 final View revealView, final boolean animated, final Runnable onCompleteRunnable, 529 final PrivateTransitionCallbacks pCb) { 530 final Resources res = mLauncher.getResources(); 531 final boolean material = Utilities.isLmpOrAbove(); 532 final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); 533 final int itemsAlphaStagger = 534 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); 535 536 final View allAppsButtonView = mLauncher.getAllAppsButton(); 537 final View toView = mLauncher.getWorkspace(); 538 539 final HashMap<View, Integer> layerViews = new HashMap<>(); 540 541 // If for some reason our views aren't initialized, don't animate 542 boolean initialized = allAppsButtonView != null; 543 544 // Cancel the current animation 545 cancelAnimation(); 546 547 // Create the workspace animation. 548 // NOTE: this call apparently also sets the state for the workspace if !animated 549 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, 550 toWorkspacePage, animated, layerViews); 551 552 if (animated && initialized) { 553 mStateAnimation = LauncherAnimUtils.createAnimatorSet(); 554 555 // Play the workspace animation 556 if (workspaceAnim != null) { 557 mStateAnimation.play(workspaceAnim); 558 } 559 560 // hideAppsCustomizeHelper is called in some cases when it is already hidden 561 // don't perform all these no-op animations. In particularly, this was causing 562 // the all-apps button to pop in and out. 563 if (fromView.getVisibility() == View.VISIBLE) { 564 int width = revealView.getMeasuredWidth(); 565 int height = revealView.getMeasuredHeight(); 566 float revealRadius = (float) Math.hypot(width / 2, height / 2); 567 revealView.setVisibility(View.VISIBLE); 568 revealView.setAlpha(1f); 569 revealView.setTranslationY(0); 570 layerViews.put(revealView, BUILD_AND_SET_LAYER); 571 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView); 572 573 // Calculate the final animation values 574 final float revealViewToXDrift; 575 final float revealViewToYDrift; 576 if (material) { 577 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView); 578 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView); 579 } else { 580 revealViewToYDrift = 2 * height / 3; 581 revealViewToXDrift = 0; 582 } 583 584 // The vertical motion of the apps panel should be delayed by one frame 585 // from the conceal animation in order to give the right feel. We correspondingly 586 // shorten the duration so that the slide and conceal end at the same time. 587 TimeInterpolator decelerateInterpolator = material ? 588 new LogDecelerateInterpolator(100, 0) : 589 new DecelerateInterpolator(1f); 590 ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY", 591 0, revealViewToYDrift); 592 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY); 593 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); 594 panelDriftY.setInterpolator(decelerateInterpolator); 595 mStateAnimation.play(panelDriftY); 596 597 ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX", 598 0, revealViewToXDrift); 599 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY); 600 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); 601 panelDriftX.setInterpolator(decelerateInterpolator); 602 mStateAnimation.play(panelDriftX); 603 604 // Setup animation for the reveal panel alpha 605 final float revealViewToAlpha = !material ? 0f : 606 pCb.getMaterialRevealViewFinalAlpha(revealView); 607 if (revealViewToAlpha != 1f) { 608 ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha", 609 1f, revealViewToAlpha); 610 panelAlpha.setDuration(material ? revealDuration : 150); 611 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); 612 panelAlpha.setInterpolator(decelerateInterpolator); 613 mStateAnimation.play(panelAlpha); 614 } 615 616 // Setup the animation for the content view 617 layerViews.put(contentView, BUILD_AND_SET_LAYER); 618 619 // Create the individual animators 620 ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY", 621 0, revealViewToYDrift); 622 contentView.setTranslationY(0); 623 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY); 624 pageDrift.setInterpolator(decelerateInterpolator); 625 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); 626 mStateAnimation.play(pageDrift); 627 628 contentView.setAlpha(1f); 629 ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f); 630 itemsAlpha.setDuration(100); 631 itemsAlpha.setInterpolator(decelerateInterpolator); 632 mStateAnimation.play(itemsAlpha); 633 634 if (material) { 635 // Animate the all apps button 636 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius(); 637 AnimatorListenerAdapter listener = 638 pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView); 639 Animator reveal = 640 LauncherAnimUtils.createCircularReveal(revealView, width / 2, 641 height / 2, revealRadius, finalRadius); 642 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); 643 reveal.setDuration(revealDuration); 644 reveal.setStartDelay(itemsAlphaStagger); 645 if (listener != null) { 646 reveal.addListener(listener); 647 } 648 mStateAnimation.play(reveal); 649 } 650 651 dispatchOnLauncherTransitionPrepare(fromView, animated, true); 652 dispatchOnLauncherTransitionPrepare(toView, animated, true); 653 } 654 655 mStateAnimation.addListener(new AnimatorListenerAdapter() { 656 @Override 657 public void onAnimationEnd(Animator animation) { 658 fromView.setVisibility(View.GONE); 659 dispatchOnLauncherTransitionEnd(fromView, animated, true); 660 dispatchOnLauncherTransitionEnd(toView, animated, true); 661 662 // Run any queued runnables 663 if (onCompleteRunnable != null) { 664 onCompleteRunnable.run(); 665 } 666 667 // Animation complete callback 668 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView); 669 670 // Disable all necessary layers 671 for (View v : layerViews.keySet()) { 672 if (layerViews.get(v) == BUILD_AND_SET_LAYER) { 673 v.setLayerType(View.LAYER_TYPE_NONE, null); 674 } 675 } 676 677 // Reset page transforms 678 if (contentView != null) { 679 contentView.setTranslationX(0); 680 contentView.setTranslationY(0); 681 contentView.setAlpha(1); 682 } 683 684 // This can hold unnecessary references to views. 685 mStateAnimation = null; 686 } 687 }); 688 689 final AnimatorSet stateAnimation = mStateAnimation; 690 final Runnable startAnimRunnable = new Runnable() { 691 public void run() { 692 // Check that mStateAnimation hasn't changed while 693 // we waited for a layout/draw pass 694 if (mStateAnimation != stateAnimation) 695 return; 696 dispatchOnLauncherTransitionStart(fromView, animated, false); 697 dispatchOnLauncherTransitionStart(toView, animated, false); 698 699 // Enable all necessary layers 700 for (View v : layerViews.keySet()) { 701 if (layerViews.get(v) == BUILD_AND_SET_LAYER) { 702 v.setLayerType(View.LAYER_TYPE_HARDWARE, null); 703 } 704 if (Utilities.isLmpOrAbove()) { 705 v.buildLayer(); 706 } 707 } 708 mStateAnimation.start(); 709 } 710 }; 711 fromView.post(startAnimRunnable); 712 } else { 713 fromView.setVisibility(View.GONE); 714 dispatchOnLauncherTransitionPrepare(fromView, animated, true); 715 dispatchOnLauncherTransitionStart(fromView, animated, true); 716 dispatchOnLauncherTransitionEnd(fromView, animated, true); 717 dispatchOnLauncherTransitionPrepare(toView, animated, true); 718 dispatchOnLauncherTransitionStart(toView, animated, true); 719 dispatchOnLauncherTransitionEnd(toView, animated, true); 720 721 // Run any queued runnables 722 if (onCompleteRunnable != null) { 723 onCompleteRunnable.run(); 724 } 725 } 726 } 727 728 729 /** 730 * Dispatches the prepare-transition event to suitable views. 731 */ 732 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) { 733 if (v instanceof LauncherTransitionable) { 734 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated, 735 toWorkspace); 736 } 737 } 738 739 /** 740 * Dispatches the start-transition event to suitable views. 741 */ 742 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) { 743 if (v instanceof LauncherTransitionable) { 744 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated, 745 toWorkspace); 746 } 747 748 // Update the workspace transition step as well 749 dispatchOnLauncherTransitionStep(v, 0f); 750 } 751 752 /** 753 * Dispatches the step-transition event to suitable views. 754 */ 755 void dispatchOnLauncherTransitionStep(View v, float t) { 756 if (v instanceof LauncherTransitionable) { 757 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t); 758 } 759 } 760 761 /** 762 * Dispatches the end-transition event to suitable views. 763 */ 764 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) { 765 if (v instanceof LauncherTransitionable) { 766 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated, 767 toWorkspace); 768 } 769 770 // Update the workspace transition step as well 771 dispatchOnLauncherTransitionStep(v, 1f); 772 } 773 774 /** 775 * Cancels the current animation. 776 */ 777 private void cancelAnimation() { 778 if (mStateAnimation != null) { 779 mStateAnimation.setDuration(0); 780 mStateAnimation.cancel(); 781 mStateAnimation = null; 782 } 783 } 784}