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