OSDN Git Service

resolved conflicts for merge of 13ef17a3 to mnc-dr-dev
[android-x86/packages-apps-Launcher3.git] / src / com / android / launcher3 / WorkspaceStateTransitionAnimation.java
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 }