Implement timings for go-to-full-shade transition.

Change-Id: Ib7c0f65b7e2565a6c30b9962e9f81547537b408a
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 127bdbe..32425ad 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -395,4 +395,8 @@
 
     <!-- Margin on the left side of the battery % when on Keyguard. -->
     <dimen name="header_battery_margin_keyguard">6dp</dimen>
+
+    <!-- Additional translation (downwards) for appearing notifications when going to the full shade
+         from Keyguard. -->
+    <dimen name="go_to_full_shade_appearing_translation">200dp</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 4d7698a..1cd18a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -43,7 +43,6 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -449,19 +448,20 @@
     }
 
     @Override
-    public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
+    public void performRemoveAnimation(long duration, float translationDirection,
+            Runnable onFinishedRunnable) {
         enableAppearDrawing(true);
         if (mDrawingAppearAnimation) {
             startAppearAnimation(false /* isAppearing */, translationDirection,
-                    0, onFinishedRunnable);
+                    0, duration, onFinishedRunnable);
         }
     }
 
     @Override
-    public void performAddAnimation(long delay) {
+    public void performAddAnimation(long delay, long duration) {
         enableAppearDrawing(true);
         if (mDrawingAppearAnimation) {
-            startAppearAnimation(true /* isAppearing */, -1.0f, delay, null);
+            startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
         }
     }
 
@@ -470,8 +470,8 @@
         mScrimView.setAlpha(scrimAmount);
     }
 
-    private void startAppearAnimation(boolean isAppearing,
-            float translationDirection, long delay, final Runnable onFinishedRunnable) {
+    private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
+            long duration, final Runnable onFinishedRunnable) {
         if (mAppearAnimator != null) {
             mAppearAnimator.cancel();
         }
@@ -501,8 +501,7 @@
                 targetValue);
         mAppearAnimator.setInterpolator(mLinearInterpolator);
         mAppearAnimator.setDuration(
-                (long) (StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR
-                        * Math.abs(mAppearAnimationFraction - targetValue)));
+                (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
         mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
index d60c17f..9712ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
@@ -116,12 +116,15 @@
     }
 
     @Override
-    public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
+    public void performRemoveAnimation(long duration, float translationDirection,
+            Runnable onFinishedRunnable) {
+        // TODO: Use duration
         performVisibilityAnimation(false);
     }
 
     @Override
-    public void performAddAnimation(long delay) {
+    public void performAddAnimation(long delay, long duration) {
+        // TODO: use delay and duration
         performVisibilityAnimation(true);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index b71cd77..5cadd1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -220,6 +220,7 @@
     /**
      * Perform a remove animation on this view.
      *
+     * @param duration The duration of the remove animation.
      * @param translationDirection The direction value from [-1 ... 1] indicating in which the
      *                             animation should be performed. A value of -1 means that The
      *                             remove animation should be performed upwards,
@@ -227,10 +228,10 @@
      *                             Should mean the opposite.
      * @param onFinishedRunnable A runnable which should be run when the animation is finished.
      */
-    public abstract void performRemoveAnimation(float translationDirection,
+    public abstract void performRemoveAnimation(long duration, float translationDirection,
             Runnable onFinishedRunnable);
 
-    public abstract void performAddAnimation(long delay);
+    public abstract void performAddAnimation(long delay, long duration);
 
     public abstract void setScrimAmount(float scrimAmount);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
index dfeadc5..d8d964e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
@@ -113,12 +113,15 @@
     }
 
     @Override
-    public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
+    public void performRemoveAnimation(long duration, float translationDirection,
+            Runnable onFinishedRunnable) {
+        // TODO: Use duration
         performVisibilityAnimation(false);
     }
 
     @Override
-    public void performAddAnimation(long delay) {
+    public void performAddAnimation(long delay, long duration) {
+        // TODO: Use delay and duration
         performVisibilityAnimation(true);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 99d3a01..2709384 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -31,6 +31,7 @@
     boolean animateDimmed;
     boolean animateDark;
     boolean hasDelays;
+    boolean hasGoToFullShadeEvent;
 
     public AnimationFilter animateAlpha() {
         animateAlpha = true;
@@ -87,6 +88,10 @@
         int size = events.size();
         for (int i = 0; i < size; i++) {
             combineFilter(events.get(i).filter);
+            if (events.get(i).animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE) {
+                hasGoToFullShadeEvent = true;
+            }
         }
     }
 
@@ -112,5 +117,6 @@
         animateDimmed = false;
         animateDark = false;
         hasDelays = false;
+        hasGoToFullShadeEvent = false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index e0167e9..1401082 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -133,6 +133,7 @@
     private boolean mDimmedNeedsAnimation;
     private boolean mDarkNeedsAnimation;
     private boolean mActivateNeedsAnimation;
+    private boolean mGoToFullShadeNeedsAnimation;
     private boolean mIsExpanded = true;
     private boolean mChildrenUpdateRequested;
     private SpeedBumpView mSpeedBumpView;
@@ -1214,6 +1215,9 @@
                 count++;
             }
         }
+        if (mDismissView.willBeGone()) {
+            count--;
+        }
         return count;
     }
 
@@ -1573,6 +1577,7 @@
         generateActivateEvent();
         generateDimmedEvent();
         generateDarkEvent();
+        generateGoToFullShadeEvent();
         mNeedsAnimation = false;
     }
 
@@ -1656,6 +1661,14 @@
         mDarkNeedsAnimation = false;
     }
 
+    private void generateGoToFullShadeEvent() {
+        if (mGoToFullShadeNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
+        }
+        mGoToFullShadeNeedsAnimation = false;
+    }
+
     private boolean onInterceptTouchEventScroll(MotionEvent ev) {
         if (!isScrollingEnabled()) {
             return false;
@@ -1923,7 +1936,6 @@
                     generateAddAnimation(mSpeedBumpView);
                 }
             } else {
-                mSpeedBumpView.performVisibilityAnimation(false);
                 generateRemoveAnimation(mSpeedBumpView);
             }
         }
@@ -1932,6 +1944,9 @@
     public void goToFullShade() {
         updateSpeedBump(true /* visibility */);
         mDismissView.setInvisible();
+        mGoToFullShadeNeedsAnimation = true;
+        mNeedsAnimation =  true;
+        requestChildrenUpdate();
     }
 
     public void cancelExpandHelper() {
@@ -2128,6 +2143,16 @@
                 // ANIMATION_TYPE_DARK
                 new AnimationFilter()
                         .animateDark(),
+
+                // ANIMATION_TYPE_GO_TO_FULL_SHADE
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateDimmed()
+                        .animateScale()
+                        .animateZ(),
         };
 
         static int[] LENGTHS = new int[] {
@@ -2161,6 +2186,9 @@
 
                 // ANIMATION_TYPE_DARK
                 StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_GO_TO_FULL_SHADE
+                StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
         };
 
         static final int ANIMATION_TYPE_ADD = 0;
@@ -2173,6 +2201,7 @@
         static final int ANIMATION_TYPE_DIMMED = 7;
         static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
         static final int ANIMATION_TYPE_DARK = 9;
+        static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
 
         final long eventStartTime;
         final View changingView;
@@ -2193,13 +2222,18 @@
          * Combines the length of several animation events into a single value.
          *
          * @param events The events of the lengths to combine.
-         * @return The combined length. This is just the maximum of all length.
+         * @return The combined length. Depending on the event types, this might be the maximum of
+         *         all events or the length of a specific event.
          */
         static long combineLength(ArrayList<AnimationEvent> events) {
             long length = 0;
             int size = events.size();
             for (int i = 0; i < size; i++) {
-                length = Math.max(length, events.get(i).length);
+                AnimationEvent event = events.get(i);
+                length = Math.max(length, event.length);
+                if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
+                    return event.length;
+                }
             }
             return length;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 71524ec..fddd3d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -39,10 +39,12 @@
 public class StackStateAnimator {
 
     public static final int ANIMATION_DURATION_STANDARD = 360;
+    public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
     public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
     public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
     public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
     public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
+    public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
     private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
 
     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
@@ -65,16 +67,19 @@
     private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
 
     private final Interpolator mFastOutSlowInInterpolator;
+    private final int mGoToFullShadeAppearingTranslation;
     public NotificationStackScrollLayout mHostLayout;
     private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
             new ArrayList<>();
     private ArrayList<View> mNewAddChildren = new ArrayList<>();
-    private Set<Animator> mAnimatorSet = new HashSet<Animator>();
-    private Stack<AnimatorListenerAdapter> mAnimationListenerPool
-            = new Stack<AnimatorListenerAdapter>();
+    private Set<Animator> mAnimatorSet = new HashSet<>();
+    private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>();
     private AnimationFilter mAnimationFilter = new AnimationFilter();
     private long mCurrentLength;
 
+    /** The current index for the last child which was not added in this event set. */
+    private int mCurrentLastNotAddedIndex;
+
     private ValueAnimator mTopOverScrollAnimator;
     private ValueAnimator mBottomOverScrollAnimator;
 
@@ -82,6 +87,9 @@
         mHostLayout = hostLayout;
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
                 android.R.interpolator.fast_out_slow_in);
+        mGoToFullShadeAppearingTranslation =
+                hostLayout.getContext().getResources().getDimensionPixelSize(
+                        R.dimen.go_to_full_shade_appearing_translation);
     }
 
     public boolean isRunning() {
@@ -97,6 +105,7 @@
         int childCount = mHostLayout.getChildCount();
         mAnimationFilter.applyCombination(mNewEvents);
         mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
+        mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
         for (int i = 0; i < childCount; i++) {
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
 
@@ -116,6 +125,22 @@
         mNewAddChildren.clear();
     }
 
+    private int findLastNotAddedIndex(StackScrollState finalState) {
+        int childCount = mHostLayout.getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+
+            StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
+            if (viewState == null || child.getVisibility() == View.GONE) {
+                continue;
+            }
+            if (!mNewAddChildren.contains(child)) {
+                return viewState.notGoneIndex;
+            }
+        }
+        return -1;
+    }
+
     /**
      * Start an animation to the given viewState
      */
@@ -139,38 +164,48 @@
         boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
                 alphaChanging || heightChanging || topInsetChanging;
         long delay = 0;
+        long duration = mCurrentLength;
         if (hasDelays && isDelayRelevant || wasAdded) {
             delay = calculateChildAnimationDelay(viewState, finalState);
         }
 
+        if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
+            child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation);
+            yTranslationChanging = true;
+            float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex;
+            longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f);
+            duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 +
+                    (long) (100 * longerDurationFactor);
+        }
+
         // start translationY animation
         if (yTranslationChanging) {
-            startYTranslationAnimation(child, viewState, delay);
+            startYTranslationAnimation(child, viewState, duration, delay);
         }
 
         // start translationZ animation
         if (zTranslationChanging) {
-            startZTranslationAnimation(child, viewState, delay);
+            startZTranslationAnimation(child, viewState, duration, delay);
         }
 
         // start scale animation
         if (scaleChanging) {
-            startScaleAnimation(child, viewState);
+            startScaleAnimation(child, viewState, duration);
         }
 
         // start alpha animation
         if (alphaChanging && child.getTranslationX() == 0) {
-            startAlphaAnimation(child, viewState, delay);
+            startAlphaAnimation(child, viewState, duration, delay);
         }
 
         // start height animation
         if (heightChanging) {
-            startHeightAnimation(child, viewState, delay);
+            startHeightAnimation(child, viewState, duration, delay);
         }
 
         // start top inset animation
         if (topInsetChanging) {
-            startInsetAnimation(child, viewState, delay);
+            startInsetAnimation(child, viewState, duration, delay);
         }
 
         // start dimmed animation
@@ -186,12 +221,15 @@
         child.setScrimAmount(viewState.scrimAmount);
 
         if (wasAdded) {
-            child.performAddAnimation(delay);
+            child.performAddAnimation(delay, mCurrentLength);
         }
     }
 
     private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
             StackScrollState finalState) {
+        if (mAnimationFilter.hasGoToFullShadeEvent) {
+            return calculateDelayGoToFullShade(viewState);
+        }
         long minDelay = 0;
         for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
             long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
@@ -236,8 +274,14 @@
         return minDelay;
     }
 
+    private long calculateDelayGoToFullShade(StackScrollState.ViewState viewState) {
+        float index = viewState.notGoneIndex;
+        index = (float) Math.pow(index, 0.7f);
+        return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
+    }
+
     private void startHeightAnimation(final ExpandableView child,
-            StackScrollState.ViewState viewState, long delay) {
+            StackScrollState.ViewState viewState, long duration, long delay) {
         Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
         Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
         int newEndValue = viewState.height;
@@ -274,7 +318,7 @@
             }
         });
         animator.setInterpolator(mFastOutSlowInInterpolator);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
             animator.setStartDelay(delay);
@@ -296,7 +340,7 @@
     }
 
     private void startInsetAnimation(final ExpandableView child,
-            StackScrollState.ViewState viewState, long delay) {
+            StackScrollState.ViewState viewState, long duration, long delay) {
         Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
         Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
         int newEndValue = viewState.clipTopAmount;
@@ -332,7 +376,7 @@
             }
         });
         animator.setInterpolator(mFastOutSlowInInterpolator);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
             animator.setStartDelay(delay);
@@ -354,7 +398,7 @@
     }
 
     private void startAlphaAnimation(final ExpandableView child,
-            final StackScrollState.ViewState viewState, long delay) {
+            final StackScrollState.ViewState viewState, long duration, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
         Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
         final float newEndValue = viewState.alpha;
@@ -413,7 +457,7 @@
                 mWasCancelled = false;
             }
         });
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
             animator.setStartDelay(delay);
@@ -433,7 +477,7 @@
     }
 
     private void startZTranslationAnimation(final ExpandableView child,
-            final StackScrollState.ViewState viewState, long delay) {
+            final StackScrollState.ViewState viewState, long duration, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
         float newEndValue = viewState.zTranslation;
@@ -463,7 +507,7 @@
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
                 child.getTranslationZ(), newEndValue);
         animator.setInterpolator(mFastOutSlowInInterpolator);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
             animator.setStartDelay(delay);
@@ -485,7 +529,7 @@
     }
 
     private void startYTranslationAnimation(final ExpandableView child,
-            StackScrollState.ViewState viewState, long delay) {
+            StackScrollState.ViewState viewState, long duration, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
         float newEndValue = viewState.yTranslation;
@@ -516,7 +560,7 @@
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
                 child.getTranslationY(), newEndValue);
         animator.setInterpolator(mFastOutSlowInInterpolator);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
             animator.setStartDelay(delay);
@@ -538,7 +582,7 @@
     }
 
     private void startScaleAnimation(final ExpandableView child,
-            StackScrollState.ViewState viewState) {
+            StackScrollState.ViewState viewState, long duration) {
         Float previousStartValue = getChildTag(child, TAG_START_SCALE);
         Float previousEndValue = getChildTag(child, TAG_END_SCALE);
         float newEndValue = viewState.scale;
@@ -573,7 +617,7 @@
                 PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue);
         ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY);
         animator.setInterpolator(mFastOutSlowInInterpolator);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         animator.addListener(getGlobalAnimationFinishedListener());
         // remove the tag when the animation is finished
@@ -637,11 +681,12 @@
     /**
      * Cancel the previous animator and get the duration of the new animation.
      *
+     * @param duration the new duration
      * @param previousAnimator the animator which was running before
      * @return the new duration
      */
-    private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator) {
-        long newDuration = mCurrentLength;
+    private long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) {
+        long newDuration = duration;
         if (previousAnimator != null) {
             // We take either the desired length of the new animation or the remaining time of
             // the previous animator, whichever is longer.
@@ -710,7 +755,8 @@
                     translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
 
                 }
-                changingView.performRemoveAnimation(translationDirection, new Runnable() {
+                changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
+                        translationDirection, new Runnable() {
                     @Override
                     public void run() {
                         // remove the temporary overlay