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