Fix jumps in the beginning of animations

If the first draw frame of an animation is
expensive, which it often is, it causes a big
jump. Added a helper class which automatically
adjusts the animation start time if the first
frame is more than 16ms.

Change-Id: I100edbc41c2abe930a32d6bcf0a782ea9735f7f9
diff --git a/src/com/android/launcher2/AppWidgetResizeFrame.java b/src/com/android/launcher2/AppWidgetResizeFrame.java
index 0fd39ae..4b54414 100644
--- a/src/com/android/launcher2/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher2/AppWidgetResizeFrame.java
@@ -444,7 +444,8 @@
                     newHeight);
             PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", lp.x, newX);
             PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
-            ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(lp, width, height, x, y);
+            ObjectAnimator oa =
+                    LauncherAnimUtils.ofPropertyValuesHolder(lp, this, width, height, x, y);
             ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, "alpha", 1.0f);
             ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, "alpha", 1.0f);
             ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, "alpha", 1.0f);
diff --git a/src/com/android/launcher2/AppsCustomizeTabHost.java b/src/com/android/launcher2/AppsCustomizeTabHost.java
index 27ceaba..225b056 100644
--- a/src/com/android/launcher2/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher2/AppsCustomizeTabHost.java
@@ -290,11 +290,7 @@
                 final AnimatorSet animSet = LauncherAnimUtils.createAnimatorSet();
                 animSet.playTogether(outAnim, inAnim);
                 animSet.setDuration(duration);
-                post(new Runnable() {
-                    public void run() {
-                        animSet.start();
-                    }
-                });
+                animSet.start();
             }
         });
     }
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 98fcdea..ddb9b64 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -241,7 +241,7 @@
 
         for (int i = 0; i < mDragOutlineAnims.length; i++) {
             final InterruptibleInOutAnimator anim =
-                new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);
+                new InterruptibleInOutAnimator(this, duration, fromAlphaValue, toAlphaValue);
             anim.getAnimator().setInterpolator(mEaseOutInterpolator);
             final int thisIndex = i;
             anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
@@ -1128,7 +1128,7 @@
                 return true;
             }
 
-            ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
+            ValueAnimator va = LauncherAnimUtils.ofFloat(child, 0f, 1f);
             va.setDuration(duration);
             mReorderAnimators.put(lp, va);
 
@@ -2324,7 +2324,7 @@
             if (finalDeltaX == 0 && finalDeltaY == 0) {
                 return;
             }
-            ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
+            ValueAnimator va = LauncherAnimUtils.ofFloat(child, 0f, 1f);
             a = va;
             va.setRepeatMode(ValueAnimator.REVERSE);
             va.setRepeatCount(ValueAnimator.INFINITE);
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index e6b15b8..b25ae61 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -80,7 +80,7 @@
         setScaleY(initialScale);
 
         // Animate the view into the correct position
-        mAnim = LauncherAnimUtils.ofFloat(0.0f, 1.0f);
+        mAnim = LauncherAnimUtils.ofFloat(this, 0f, 1f);
         mAnim.setDuration(150);
         mAnim.addUpdateListener(new AnimatorUpdateListener() {
             @Override
@@ -203,7 +203,7 @@
     }
 
     public void crossFade(int duration) {
-        ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
+        ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1f);
         va.setDuration(duration);
         va.setInterpolator(new DecelerateInterpolator(1.5f));
         va.addUpdateListener(new AnimatorUpdateListener() {
diff --git a/src/com/android/launcher2/FirstFrameAnimatorHelper.java b/src/com/android/launcher2/FirstFrameAnimatorHelper.java
new file mode 100644
index 0000000..f42a741
--- /dev/null
+++ b/src/com/android/launcher2/FirstFrameAnimatorHelper.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.animation.ValueAnimator;
+import android.animation.Animator.AnimatorListener;
+import android.util.Log;
+import android.view.ViewTreeObserver;
+import android.view.View;
+
+/*
+ *  This is a helper class that listens to updates from the corresponding animation.
+ *  For the first two frames, it adjusts the current play time of the animation to
+ *  prevent jank at the beginning of the animation
+ */
+public class FirstFrameAnimatorHelper implements ValueAnimator.AnimatorUpdateListener {
+    private static final boolean DEBUG = false;
+    private static final int MAX_FIRST_FRAME_DELAY = 200;
+    private static final int IDEAL_FRAME_DURATION = 16;
+    private View mTarget;
+    private long mStartFrame;
+    private long mStartTime = -1;
+    private boolean mHandlingOnAnimationUpdate;
+    private boolean mAdjustedSecondFrameTime;
+
+    private static ViewTreeObserver.OnDrawListener sGlobalDrawListener;
+    private static long sGlobalFrameCounter;
+
+    public FirstFrameAnimatorHelper(ValueAnimator animator, View target) {
+        mTarget = target;
+        animator.addUpdateListener(this);
+    }
+
+    public static void initializeDrawListener(View view) {
+        sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
+                private long mTime = System.currentTimeMillis();
+                public void onDraw() {
+                    sGlobalFrameCounter++;
+                    long newTime = System.currentTimeMillis();
+                    if (DEBUG) {
+                        Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
+                    }
+                    mTime = newTime;
+                }
+            };
+        view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
+    }
+
+    public void onAnimationUpdate(ValueAnimator animation) {
+        if (mStartTime == -1) {
+            mStartFrame = sGlobalFrameCounter;
+            mStartTime = System.currentTimeMillis();
+        }
+
+        if (!mHandlingOnAnimationUpdate) {
+            mHandlingOnAnimationUpdate = true;
+            long frameNum = sGlobalFrameCounter - mStartFrame;
+
+            // If we haven't drawn our first frame, reset the time to t = 0
+            // (give up after 200ms of waiting though - might happen, for example, if we are no
+            // longer in the foreground and no frames are being rendered ever)
+            if (frameNum == 0 && System.currentTimeMillis() < mStartTime + MAX_FIRST_FRAME_DELAY) {
+                mTarget.getRootView().invalidate(); // make sure we'll do a draw
+                animation.setCurrentPlayTime(0);
+
+            // For the second frame, if the first frame took more than 16ms,
+            // adjust the start time and pretend it took only 16ms anyway. This
+            // prevents a large jump in the animation due to an expensive first frame
+            } else if (frameNum == 1 && !mAdjustedSecondFrameTime &&
+                       System.currentTimeMillis() > mStartTime + 16) {
+                animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
+                mAdjustedSecondFrameTime = true;
+            } else {
+                if (frameNum > 1) {
+                    animation.removeUpdateListener(this);
+                }
+                if (DEBUG) print(animation);
+            }
+            mHandlingOnAnimationUpdate = false;
+        } else {
+            if (DEBUG) print(animation);
+        }
+    }
+
+    public void print(ValueAnimator animation) {
+        float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
+        Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
+              "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
+              mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
+    }
+}
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 5feac2f..5520e23 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -444,15 +444,7 @@
         });
         oa.setDuration(mExpandDuration);
         setLayerType(LAYER_TYPE_HARDWARE, null);
-        buildLayer();
-        post(new Runnable() {
-            public void run() {
-                // Check if the animator changed in the meantime
-                if (oa != mOpenCloseAnimator)
-                    return;
-                oa.start();
-            }
-        });
+        oa.start();
     }
 
     private void sendCustomAccessibilityEvent(int type, String text) {
@@ -497,15 +489,7 @@
         });
         oa.setDuration(mExpandDuration);
         setLayerType(LAYER_TYPE_HARDWARE, null);
-        buildLayer();
-        post(new Runnable() {
-            public void run() {
-                // Check if the animator changed in the meantime
-                if (oa != mOpenCloseAnimator)
-                    return;
-                oa.start();
-            }
-        });
+        oa.start();
     }
 
     void notifyDataSetChanged() {
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 05921dc..76374d2 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -199,7 +199,7 @@
             if (mNeutralAnimator != null) {
                 mNeutralAnimator.cancel();
             }
-            mAcceptAnimator = LauncherAnimUtils.ofFloat(0f, 1f);
+            mAcceptAnimator = LauncherAnimUtils.ofFloat(mCellLayout, 0f, 1f);
             mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
 
             final int previewSize = sPreviewSize;
@@ -228,7 +228,7 @@
             if (mAcceptAnimator != null) {
                 mAcceptAnimator.cancel();
             }
-            mNeutralAnimator = LauncherAnimUtils.ofFloat(0f, 1f);
+            mNeutralAnimator = LauncherAnimUtils.ofFloat(mCellLayout, 0f, 1f);
             mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
 
             final int previewSize = sPreviewSize;
@@ -573,7 +573,7 @@
         final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2;
         mAnimParams.drawable = d;
 
-        ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1.0f);
+        ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1.0f);
         va.addUpdateListener(new AnimatorUpdateListener(){
             public void onAnimationUpdate(ValueAnimator animation) {
                 float progress = (Float) animation.getAnimatedValue();
diff --git a/src/com/android/launcher2/InterruptibleInOutAnimator.java b/src/com/android/launcher2/InterruptibleInOutAnimator.java
index 9831ba3..375fddc 100644
--- a/src/com/android/launcher2/InterruptibleInOutAnimator.java
+++ b/src/com/android/launcher2/InterruptibleInOutAnimator.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.view.View;
 
 /**
  * A convenience class for two-way animations, e.g. a fadeIn/fadeOut animation.
@@ -44,8 +45,8 @@
     // TODO: This isn't really necessary, but is here to help diagnose a bug in the drag viz
     private int mDirection = STOPPED;
 
-    public InterruptibleInOutAnimator(long duration, float fromValue, float toValue) {
-        mAnimator = LauncherAnimUtils.ofFloat(fromValue, toValue).setDuration(duration);
+    public InterruptibleInOutAnimator(View view, long duration, float fromValue, float toValue) {
+        mAnimator = LauncherAnimUtils.ofFloat(view, fromValue, toValue).setDuration(duration);
         mOriginalDuration = duration;
         mOriginalFromValue = fromValue;
         mOriginalToValue = toValue;
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index a4ed91f..545ee2e 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -1240,7 +1240,7 @@
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(Intent.ACTION_USER_PRESENT);
         registerReceiver(mReceiver, filter);
-
+        FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
         mAttached = true;
         mVisible = true;
     }
@@ -1269,10 +1269,9 @@
                 final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
                 // We want to let Launcher draw itself at least once before we force it to build
                 // layers on all the workspace pages, so that transitioning to Launcher from other
-                // apps is nice and speedy. Usually the first call to preDraw doesn't correspond to
-                // a true draw so we wait until the second preDraw call to be safe
-                observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
-                    public boolean onPreDraw() {
+                // apps is nice and speedy.
+                observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
+                    public void onDraw() {
                         // We delay the layer building a bit in order to give
                         // other message processing a time to run.  In particular
                         // this avoids a delay in hiding the IME if it was
@@ -1280,8 +1279,8 @@
                         // some communication back with the app.
                         mWorkspace.postDelayed(mBuildLayersRunnable, 500);
 
-                        observer.removeOnPreDrawListener(this);
-                        return true;
+                        observer.removeOnDrawListener(this);
+                        return;
                     }
                 });
             }
@@ -2483,6 +2482,7 @@
      */
     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
         if (mStateAnimation != null) {
+            mStateAnimation.setDuration(0);
             mStateAnimation.cancel();
             mStateAnimation = null;
         }
@@ -2513,7 +2513,7 @@
 
             toView.setVisibility(View.VISIBLE);
             toView.setAlpha(0f);
-            final ObjectAnimator alphaAnim = ObjectAnimator
+            final ObjectAnimator alphaAnim = LauncherAnimUtils
                 .ofFloat(toView, "alpha", 0f, 1f)
                 .setDuration(fadeDuration);
             alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
@@ -2578,7 +2578,6 @@
             }
 
             boolean delayAnim = false;
-            final ViewTreeObserver observer;
 
             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
@@ -2588,10 +2587,7 @@
             if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) ||
                     (mWorkspace.getMeasuredWidth() == 0) ||
                     (toView.getMeasuredWidth() == 0)) {
-                observer = mWorkspace.getViewTreeObserver();
                 delayAnim = true;
-            } else {
-                observer = null;
             }
 
             final AnimatorSet stateAnimation = mStateAnimation;
@@ -2604,25 +2600,17 @@
                     setPivotsForZoom(toView, scale);
                     dispatchOnLauncherTransitionStart(fromView, animated, false);
                     dispatchOnLauncherTransitionStart(toView, animated, false);
-                    toView.post(new Runnable() {
-                        public void run() {
-                            // Check that mStateAnimation hasn't changed while
-                            // we waited for a layout/draw pass
-                            if (mStateAnimation != stateAnimation)
-                                return;
-                            mStateAnimation.start();
-                        }
-                    });
+                    LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
                 }
             };
             if (delayAnim) {
-                final OnGlobalLayoutListener delayedStart = new OnGlobalLayoutListener() {
-                    public void onGlobalLayout() {
-                        toView.post(startAnimRunnable);
-                        observer.removeOnGlobalLayoutListener(this);
-                    }
-                };
-                observer.addOnGlobalLayoutListener(delayedStart);
+                final ViewTreeObserver observer = toView.getViewTreeObserver();
+                observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+                        public void onGlobalLayout() {
+                            startAnimRunnable.run();
+                            toView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                        }
+                    });
             } else {
                 startAnimRunnable.run();
             }
@@ -2663,6 +2651,7 @@
             final boolean springLoaded, final Runnable onCompleteRunnable) {
 
         if (mStateAnimation != null) {
+            mStateAnimation.setDuration(0);
             mStateAnimation.cancel();
             mStateAnimation = null;
         }
@@ -2697,7 +2686,7 @@
                 setDuration(duration).
                 setInterpolator(new Workspace.ZoomInInterpolator());
 
-            final ObjectAnimator alphaAnim = ObjectAnimator
+            final ObjectAnimator alphaAnim = LauncherAnimUtils
                 .ofFloat(fromView, "alpha", 1f, 0f)
                 .setDuration(fadeOutDuration);
             alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
@@ -2740,14 +2729,7 @@
             }
             dispatchOnLauncherTransitionStart(fromView, animated, true);
             dispatchOnLauncherTransitionStart(toView, animated, true);
-            final Animator stateAnimation = mStateAnimation;
-            mWorkspace.post(new Runnable() {
-                public void run() {
-                    if (stateAnimation != mStateAnimation)
-                        return;
-                    mStateAnimation.start();
-                }
-            });
+            LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
         } else {
             fromView.setVisibility(View.GONE);
             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
diff --git a/src/com/android/launcher2/LauncherAnimUtils.java b/src/com/android/launcher2/LauncherAnimUtils.java
index 9317c31..5055d35 100644
--- a/src/com/android/launcher2/LauncherAnimUtils.java
+++ b/src/com/android/launcher2/LauncherAnimUtils.java
@@ -21,6 +21,8 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
+import android.view.View;
+import android.view.ViewTreeObserver;
 
 import java.util.HashSet;
 
@@ -47,6 +49,22 @@
         a.addListener(sEndAnimListener);
     }
 
+    // Helper method. Assumes a draw is pending, and that if the animation's duration is 0
+    // it should be cancelled
+    public static void startAnimationAfterNextDraw(final Animator animator, final View view) {
+        final ViewTreeObserver observer = view.getViewTreeObserver();
+        observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
+                public void onDraw() {
+                    // Use this as a signal that the animation was cancelled
+                    if (animator.getDuration() == 0) {
+                        return;
+                    }
+                    animator.start();
+                    view.getViewTreeObserver().removeOnDrawListener(this);
+                }
+            });
+    }
+
     public static void onDestroyActivity() {
         HashSet<Animator> animators = new HashSet<Animator>(sAnimators);
         for (Animator a : animators) {
@@ -64,28 +82,40 @@
         return anim;
     }
 
-    public static ValueAnimator ofFloat(float... values) {
+    public static ValueAnimator ofFloat(View target, float... values) {
         ValueAnimator anim = new ValueAnimator();
         anim.setFloatValues(values);
         cancelOnDestroyActivity(anim);
         return anim;
     }
 
-    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
+    public static ObjectAnimator ofFloat(View target, String propertyName, float... values) {
         ObjectAnimator anim = new ObjectAnimator();
         anim.setTarget(target);
         anim.setPropertyName(propertyName);
         anim.setFloatValues(values);
         cancelOnDestroyActivity(anim);
+        new FirstFrameAnimatorHelper(anim, target);
         return anim;
     }
 
-    public static ObjectAnimator ofPropertyValuesHolder(Object target,
+    public static ObjectAnimator ofPropertyValuesHolder(View target,
             PropertyValuesHolder... values) {
         ObjectAnimator anim = new ObjectAnimator();
         anim.setTarget(target);
         anim.setValues(values);
         cancelOnDestroyActivity(anim);
+        new FirstFrameAnimatorHelper(anim, target);
+        return anim;
+    }
+
+    public static ObjectAnimator ofPropertyValuesHolder(Object target,
+            View view, PropertyValuesHolder... values) {
+        ObjectAnimator anim = new ObjectAnimator();
+        anim.setTarget(target);
+        anim.setValues(values);
+        cancelOnDestroyActivity(anim);
+        new FirstFrameAnimatorHelper(anim, view);
         return anim;
     }
 }
diff --git a/src/com/android/launcher2/LauncherViewPropertyAnimator.java b/src/com/android/launcher2/LauncherViewPropertyAnimator.java
index 3ffc418..274ff80 100644
--- a/src/com/android/launcher2/LauncherViewPropertyAnimator.java
+++ b/src/com/android/launcher2/LauncherViewPropertyAnimator.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.view.ViewPropertyAnimator;
 import android.view.View;
 
@@ -123,6 +124,12 @@
 
     @Override
     public void onAnimationStart(Animator animation) {
+	// This is the first time we get a handle to the internal ValueAnimator
+	// used by the ViewPropertyAnimator.
+	// FirstFrameAnimatorHelper hooks itself up to the updates on the animator,
+	// and then adjusts the play time to keep the first two frames jank-free
+        new FirstFrameAnimatorHelper((ValueAnimator) animation, mTarget)
+                .onAnimationUpdate((ValueAnimator) animation);
         for (int i = 0; i < mListeners.size(); i++) {
             Animator.AnimatorListener listener = mListeners.get(i);
             listener.onAnimationStart(this);
diff --git a/src/com/android/launcher2/SearchDropTargetBar.java b/src/com/android/launcher2/SearchDropTargetBar.java
index fada48a..a5b76c9 100644
--- a/src/com/android/launcher2/SearchDropTargetBar.java
+++ b/src/com/android/launcher2/SearchDropTargetBar.java
@@ -77,7 +77,6 @@
         // Enable the hw layers before the animation starts (will be disabled in the onAnimationEnd
         // callback below)
         v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-        v.buildLayer();
     }
 
     private void setupAnimation(ObjectAnimator anim, final View v) {
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index cb9ac55..7d7ca86 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -1146,7 +1146,8 @@
         float startAlpha = getBackgroundAlpha();
         if (finalAlpha != startAlpha) {
             if (animated) {
-                mBackgroundFadeOutAnimation = LauncherAnimUtils.ofFloat(startAlpha, finalAlpha);
+                mBackgroundFadeOutAnimation =
+                        LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha);
                 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
                     public void onAnimationUpdate(ValueAnimator animation) {
                         setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
@@ -1687,7 +1688,8 @@
                     }
                     if (mOldBackgroundAlphas[i] != 0 ||
                         mNewBackgroundAlphas[i] != 0) {
-                        ValueAnimator bgAnim = LauncherAnimUtils.ofFloat(0f, 1f).setDuration(duration);
+                        ValueAnimator bgAnim =
+                                LauncherAnimUtils.ofFloat(cl, 0f, 1f).setDuration(duration);
                         bgAnim.setInterpolator(mZoomInInterpolator);
                         bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
                                 public void onAnimationUpdate(float a, float b) {