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 17 package com.android.launcher3; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.AnimatorSet; 22 import android.animation.ObjectAnimator; 23 import android.animation.TimeInterpolator; 24 import android.animation.ValueAnimator; 25 import android.content.Context; 26 import android.content.res.Resources; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.view.accessibility.AccessibilityManager; 30 import android.view.accessibility.AccessibilityNodeInfo; 31 import android.view.animation.DecelerateInterpolator; 32 33 import com.android.launcher3.util.Thunk; 34 35 import java.util.HashMap; 36 37 /** 38 * A convenience class to update a view's visibility state after an alpha animation. 39 */ 40 class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimator.AnimatorUpdateListener { 41 private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; 42 43 private View mView; 44 private boolean mAccessibilityEnabled; 45 46 public AlphaUpdateListener(View v, boolean accessibilityEnabled) { 47 mView = v; 48 mAccessibilityEnabled = accessibilityEnabled; 49 } 50 51 @Override 52 public void onAnimationUpdate(ValueAnimator arg0) { 53 updateVisibility(mView, mAccessibilityEnabled); 54 } 55 56 public static void updateVisibility(View view, boolean accessibilityEnabled) { 57 // We want to avoid the extra layout pass by setting the views to GONE unless 58 // accessibility is on, in which case not setting them to GONE causes a glitch. 59 int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE; 60 if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { 61 view.setVisibility(invisibleState); 62 } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD 63 && view.getVisibility() != View.VISIBLE) { 64 view.setVisibility(View.VISIBLE); 65 } 66 } 67 68 @Override 69 public void onAnimationEnd(Animator arg0) { 70 updateVisibility(mView, mAccessibilityEnabled); 71 } 72 73 @Override 74 public void onAnimationStart(Animator arg0) { 75 // We want the views to be visible for animation, so fade-in/out is visible 76 mView.setVisibility(View.VISIBLE); 77 } 78 } 79 80 /** 81 * This interpolator emulates the rate at which the perceived scale of an object changes 82 * as its distance from a camera increases. When this interpolator is applied to a scale 83 * animation on a view, it evokes the sense that the object is shrinking due to moving away 84 * from the camera. 85 */ 86 class ZInterpolator implements TimeInterpolator { 87 private float focalLength; 88 89 public ZInterpolator(float foc) { 90 focalLength = foc; 91 } 92 93 public float getInterpolation(float input) { 94 return (1.0f - focalLength / (focalLength + input)) / 95 (1.0f - focalLength / (focalLength + 1.0f)); 96 } 97 } 98 99 /** 100 * The exact reverse of ZInterpolator. 101 */ 102 class InverseZInterpolator implements TimeInterpolator { 103 private ZInterpolator zInterpolator; 104 public InverseZInterpolator(float foc) { 105 zInterpolator = new ZInterpolator(foc); 106 } 107 public float getInterpolation(float input) { 108 return 1 - zInterpolator.getInterpolation(1 - input); 109 } 110 } 111 112 /** 113 * InverseZInterpolator compounded with an ease-out. 114 */ 115 class ZoomInInterpolator implements TimeInterpolator { 116 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f); 117 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f); 118 119 public float getInterpolation(float input) { 120 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input)); 121 } 122 } 123 124 /** 125 * Stores the transition states for convenience. 126 */ 127 class TransitionStates { 128 129 // Raw states 130 final boolean oldStateIsNormal; 131 final boolean oldStateIsSpringLoaded; 132 final boolean oldStateIsNormalHidden; 133 final boolean oldStateIsOverviewHidden; 134 final boolean oldStateIsOverview; 135 136 final boolean stateIsNormal; 137 final boolean stateIsSpringLoaded; 138 final boolean stateIsNormalHidden; 139 final boolean stateIsOverviewHidden; 140 final boolean stateIsOverview; 141 142 // Convenience members 143 final boolean workspaceToAllApps; 144 final boolean overviewToAllApps; 145 final boolean allAppsToWorkspace; 146 final boolean workspaceToOverview; 147 final boolean overviewToWorkspace; 148 149 public TransitionStates(final Workspace.State fromState, final Workspace.State toState) { 150 oldStateIsNormal = (fromState == Workspace.State.NORMAL); 151 oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED); 152 oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN); 153 oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN); 154 oldStateIsOverview = (fromState == Workspace.State.OVERVIEW); 155 156 stateIsNormal = (toState == Workspace.State.NORMAL); 157 stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED); 158 stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN); 159 stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN); 160 stateIsOverview = (toState == Workspace.State.OVERVIEW); 161 162 workspaceToOverview = (oldStateIsNormal && stateIsOverview); 163 workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden); 164 overviewToWorkspace = (oldStateIsOverview && stateIsNormal); 165 overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden); 166 allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal); 167 } 168 } 169 170 /** 171 * Manages the animations between each of the workspace states. 172 */ 173 public class WorkspaceStateTransitionAnimation { 174 175 public static final String TAG = "WorkspaceStateTransitionAnimation"; 176 177 public static final int SCROLL_TO_CURRENT_PAGE = -1; 178 @Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350; 179 180 final @Thunk Launcher mLauncher; 181 final @Thunk Workspace mWorkspace; 182 183 @Thunk AnimatorSet mStateAnimator; 184 @Thunk float[] mOldBackgroundAlphas; 185 @Thunk float[] mOldAlphas; 186 @Thunk float[] mNewBackgroundAlphas; 187 @Thunk float[] mNewAlphas; 188 @Thunk int mLastChildCount = -1; 189 190 @Thunk float mCurrentScale; 191 @Thunk float mNewScale; 192 193 @Thunk final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); 194 195 @Thunk float mSpringLoadedShrinkFactor; 196 @Thunk float mOverviewModeShrinkFactor; 197 @Thunk float mWorkspaceScrimAlpha; 198 @Thunk int mAllAppsTransitionTime; 199 @Thunk int mOverviewTransitionTime; 200 @Thunk int mOverlayTransitionTime; 201 @Thunk boolean mWorkspaceFadeInAdjacentScreens; 202 203 public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) { 204 mLauncher = launcher; 205 mWorkspace = workspace; 206 207 DeviceProfile grid = mLauncher.getDeviceProfile(); 208 Resources res = launcher.getResources(); 209 mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime); 210 mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime); 211 mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime); 212 mSpringLoadedShrinkFactor = 213 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f; 214 mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f; 215 mOverviewModeShrinkFactor = grid.getOverviewModeScale(Utilities.isRtl(res)); 216 mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); 217 } 218 219 public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState, 220 int toPage, boolean animated, boolean hasOverlaySearchBar, 221 HashMap<View, Integer> layerViews) { 222 AccessibilityManager am = (AccessibilityManager) 223 mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); 224 final boolean accessibilityEnabled = am.isEnabled(); 225 TransitionStates states = new TransitionStates(fromState, toState); 226 int duration = getAnimationDuration(states); 227 animateWorkspace(states, toPage, animated, duration, layerViews, 228 accessibilityEnabled); 229 animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews, 230 accessibilityEnabled); 231 animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION); 232 return mStateAnimator; 233 } 234 235 public float getFinalScale() { 236 return mNewScale; 237 } 238 239 /** 240 * Reinitializes the arrays that we need for the animations on each page. 241 */ 242 private void reinitializeAnimationArrays() { 243 final int childCount = mWorkspace.getChildCount(); 244 if (mLastChildCount == childCount) return; 245 246 mOldBackgroundAlphas = new float[childCount]; 247 mOldAlphas = new float[childCount]; 248 mNewBackgroundAlphas = new float[childCount]; 249 mNewAlphas = new float[childCount]; 250 } 251 252 /** 253 * Returns the proper animation duration for a transition. 254 */ 255 private int getAnimationDuration(TransitionStates states) { 256 if (states.workspaceToAllApps || states.overviewToAllApps) { 257 return mAllAppsTransitionTime; 258 } else if (states.workspaceToOverview || states.overviewToWorkspace) { 259 return mOverviewTransitionTime; 260 } else { 261 return mOverlayTransitionTime; 262 } 263 } 264 265 /** 266 * Starts a transition animation for the workspace. 267 */ 268 private void animateWorkspace(final TransitionStates states, int toPage, final boolean animated, 269 final int duration, final HashMap<View, Integer> layerViews, 270 final boolean accessibilityEnabled) { 271 // Reinitialize animation arrays for the current workspace state 272 reinitializeAnimationArrays(); 273 274 // Cancel existing workspace animations and create a new animator set if requested 275 cancelAnimation(); 276 if (animated) { 277 mStateAnimator = LauncherAnimUtils.createAnimatorSet(); 278 } 279 280 // Update the workspace state 281 float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ? 282 1.0f : 0f; 283 float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ? 284 1f : 0f; 285 float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f; 286 float finalWorkspaceTranslationY = states.stateIsOverview || states.stateIsOverviewHidden ? 287 mWorkspace.getOverviewModeTranslationY() : 0; 288 289 final int childCount = mWorkspace.getChildCount(); 290 final int customPageCount = mWorkspace.numCustomPages(); 291 292 mNewScale = 1.0f; 293 294 if (states.oldStateIsOverview) { 295 mWorkspace.disableFreeScroll(); 296 } else if (states.stateIsOverview) { 297 mWorkspace.enableFreeScroll(); 298 } 299 300 if (!states.stateIsNormal) { 301 if (states.stateIsSpringLoaded) { 302 mNewScale = mSpringLoadedShrinkFactor; 303 } else if (states.stateIsOverview || states.stateIsOverviewHidden) { 304 mNewScale = mOverviewModeShrinkFactor; 305 } 306 } 307 308 if (toPage == SCROLL_TO_CURRENT_PAGE) { 309 toPage = mWorkspace.getPageNearestToCenterOfScreen(); 310 } 311 mWorkspace.snapToPage(toPage, duration, mZoomInInterpolator); 312 313 for (int i = 0; i < childCount; i++) { 314 final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); 315 boolean isCurrentPage = (i == toPage); 316 float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); 317 float finalAlpha; 318 if (states.stateIsNormalHidden || states.stateIsOverviewHidden) { 319 finalAlpha = 0f; 320 } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) { 321 finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f; 322 } else { 323 finalAlpha = 1f; 324 } 325 326 // If we are animating to/from the small state, then hide the side pages and fade the 327 // current page in 328 if (!mWorkspace.isSwitchingState()) { 329 if (states.workspaceToAllApps || states.allAppsToWorkspace) { 330 if (states.allAppsToWorkspace && isCurrentPage) { 331 initialAlpha = 0f; 332 } else if (!isCurrentPage) { 333 initialAlpha = finalAlpha = 0f; 334 } 335 cl.setShortcutAndWidgetAlpha(initialAlpha); 336 } 337 } 338 339 mOldAlphas[i] = initialAlpha; 340 mNewAlphas[i] = finalAlpha; 341 if (animated) { 342 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha(); 343 mNewBackgroundAlphas[i] = finalBackgroundAlpha; 344 } else { 345 cl.setBackgroundAlpha(finalBackgroundAlpha); 346 cl.setShortcutAndWidgetAlpha(finalAlpha); 347 } 348 } 349 350 final ViewGroup overviewPanel = mLauncher.getOverviewPanel(); 351 final View hotseat = mLauncher.getHotseat(); 352 final View pageIndicator = mWorkspace.getPageIndicator(); 353 if (animated) { 354 LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mWorkspace); 355 scale.scaleX(mNewScale) 356 .scaleY(mNewScale) 357 .translationY(finalWorkspaceTranslationY) 358 .setDuration(duration) 359 .setInterpolator(mZoomInInterpolator); 360 mStateAnimator.play(scale); 361 for (int index = 0; index < childCount; index++) { 362 final int i = index; 363 final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); 364 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha(); 365 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) { 366 cl.setBackgroundAlpha(mNewBackgroundAlphas[i]); 367 cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); 368 } else { 369 if (layerViews != null) { 370 layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER); 371 } 372 if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { 373 LauncherViewPropertyAnimator alphaAnim = 374 new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets()); 375 alphaAnim.alpha(mNewAlphas[i]) 376 .setDuration(duration) 377 .setInterpolator(mZoomInInterpolator); 378 mStateAnimator.play(alphaAnim); 379 } 380 if (mOldBackgroundAlphas[i] != 0 || 381 mNewBackgroundAlphas[i] != 0) { 382 ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha", 383 mOldBackgroundAlphas[i], mNewBackgroundAlphas[i]); 384 LauncherAnimUtils.ofFloat(cl, 0f, 1f); 385 bgAnim.setInterpolator(mZoomInInterpolator); 386 bgAnim.setDuration(duration); 387 mStateAnimator.play(bgAnim); 388 } 389 } 390 } 391 Animator pageIndicatorAlpha; 392 if (pageIndicator != null) { 393 pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator) 394 .alpha(finalHotseatAndPageIndicatorAlpha).withLayer(); 395 pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator, 396 accessibilityEnabled)); 397 } else { 398 // create a dummy animation so we don't need to do null checks later 399 pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0); 400 } 401 402 LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat) 403 .alpha(finalHotseatAndPageIndicatorAlpha); 404 hotseatAlpha.addListener(new AlphaUpdateListener(hotseat, accessibilityEnabled)); 405 406 LauncherViewPropertyAnimator overviewPanelAlpha = 407 new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha); 408 overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel, 409 accessibilityEnabled)); 410 411 // For animation optimations, we may need to provide the Launcher transition 412 // with a set of views on which to force build layers in certain scenarios. 413 hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null); 414 overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); 415 if (layerViews != null) { 416 // If layerViews is not null, we add these views, and indicate that 417 // the caller can manage layer state. 418 layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); 419 layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); 420 } else { 421 // Otherwise let the animator handle layer management. 422 hotseatAlpha.withLayer(); 423 overviewPanelAlpha.withLayer(); 424 } 425 426 if (states.workspaceToOverview) { 427 pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2)); 428 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2)); 429 overviewPanelAlpha.setInterpolator(null); 430 } else if (states.overviewToWorkspace) { 431 pageIndicatorAlpha.setInterpolator(null); 432 hotseatAlpha.setInterpolator(null); 433 overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); 434 } 435 436 overviewPanelAlpha.setDuration(duration); 437 pageIndicatorAlpha.setDuration(duration); 438 hotseatAlpha.setDuration(duration); 439 440 mStateAnimator.play(overviewPanelAlpha); 441 mStateAnimator.play(hotseatAlpha); 442 mStateAnimator.play(pageIndicatorAlpha); 443 mStateAnimator.addListener(new AnimatorListenerAdapter() { 444 @Override 445 public void onAnimationEnd(Animator animation) { 446 mStateAnimator = null; 447 448 if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) { 449 overviewPanel.getChildAt(0).performAccessibilityAction( 450 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); 451 } 452 } 453 }); 454 } else { 455 overviewPanel.setAlpha(finalOverviewPanelAlpha); 456 AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled); 457 hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha); 458 AlphaUpdateListener.updateVisibility(hotseat, accessibilityEnabled); 459 if (pageIndicator != null) { 460 pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha); 461 AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled); 462 } 463 mWorkspace.updateCustomContentVisibility(); 464 mWorkspace.setScaleX(mNewScale); 465 mWorkspace.setScaleY(mNewScale); 466 mWorkspace.setTranslationY(finalWorkspaceTranslationY); 467 468 if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) { 469 overviewPanel.getChildAt(0).performAccessibilityAction( 470 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); 471 } 472 } 473 } 474 475 /** 476 * Coordinates with the workspace animation to animate the search bar. 477 * 478 * TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the 479 * bar has no idea that it is hidden, and this has no idea what state the bar is 480 * actually in. 481 */ 482 private void animateSearchBar(TransitionStates states, boolean animated, int duration, 483 boolean hasOverlaySearchBar, final HashMap<View, Integer> layerViews, 484 final boolean accessibilityEnabled) { 485 486 // The search bar is only visible in the workspace 487 final View searchBar = mLauncher.getOrCreateQsbBar(); 488 if (searchBar != null) { 489 final boolean searchBarWillBeShown = states.stateIsNormal; 490 final float finalSearchBarAlpha = searchBarWillBeShown ? 1f : 0f; 491 if (animated) { 492 if (hasOverlaySearchBar) { 493 // If there is an overlay search bar, then we will coordinate with it. 494 mStateAnimator.addListener(new AnimatorListenerAdapter() { 495 @Override 496 public void onAnimationStart(Animator animation) { 497 // If we are transitioning to a visible search bar, show it immediately 498 // and let the overlay search bar has faded out 499 if (searchBarWillBeShown) { 500 searchBar.setAlpha(finalSearchBarAlpha); 501 AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); 502 } 503 } 504 505 @Override 506 public void onAnimationEnd(Animator animation) { 507 // If we are transitioning to a hidden search bar, hide it only after 508 // the overlay search bar has faded in 509 if (!searchBarWillBeShown) { 510 searchBar.setAlpha(finalSearchBarAlpha); 511 AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); 512 } 513 } 514 }); 515 } else { 516 // Otherwise, we can just do the normal animation 517 LauncherViewPropertyAnimator searchBarAlpha = 518 new LauncherViewPropertyAnimator(searchBar).alpha(finalSearchBarAlpha); 519 searchBarAlpha.addListener(new AlphaUpdateListener(searchBar, 520 accessibilityEnabled)); 521 searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); 522 if (layerViews != null) { 523 // If layerViews is not null, we add these views, and indicate that 524 // the caller can manage layer state. 525 layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); 526 } else { 527 // Otherwise let the animator handle layer management. 528 searchBarAlpha.withLayer(); 529 } 530 searchBarAlpha.setDuration(duration); 531 mStateAnimator.play(searchBarAlpha); 532 } 533 } else { 534 // Set the search bar state immediately 535 searchBar.setAlpha(finalSearchBarAlpha); 536 AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); 537 } 538 } 539 } 540 541 /** 542 * Animates the background scrim. Add to the state animator to prevent jankiness. 543 * 544 * @param finalAlpha the final alpha for the background scrim 545 * @param animated whether or not to set the background alpha immediately 546 * @duration duration of the animation 547 */ 548 private void animateBackgroundGradient(TransitionStates states, 549 boolean animated, int duration) { 550 551 final DragLayer dragLayer = mLauncher.getDragLayer(); 552 final float startAlpha = dragLayer.getBackgroundAlpha(); 553 float finalAlpha = states.stateIsNormal ? 0 : mWorkspaceScrimAlpha; 554 555 if (finalAlpha != startAlpha) { 556 if (animated) { 557 // These properties refer to the background protection gradient used for AllApps 558 // and Widget tray. 559 ValueAnimator bgFadeOutAnimation = 560 LauncherAnimUtils.ofFloat(mWorkspace, startAlpha, finalAlpha); 561 bgFadeOutAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 562 @Override 563 public void onAnimationUpdate(ValueAnimator animation) { 564 dragLayer.setBackgroundAlpha( 565 ((Float)animation.getAnimatedValue()).floatValue()); 566 } 567 }); 568 bgFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); 569 bgFadeOutAnimation.setDuration(duration); 570 mStateAnimator.play(bgFadeOutAnimation); 571 } else { 572 dragLayer.setBackgroundAlpha(finalAlpha); 573 } 574 } 575 } 576 577 /** 578 * Cancels the current animation. 579 */ 580 private void cancelAnimation() { 581 if (mStateAnimator != null) { 582 mStateAnimator.setDuration(0); 583 mStateAnimator.cancel(); 584 } 585 mStateAnimator = null; 586 } 587 }