Merge "Make fragment animations work when fragments go away"
diff --git a/api/current.xml b/api/current.xml
index e28bb58..f65226ed 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -202631,6 +202631,19 @@
 <parameter name="drawingTime" type="long">
 </parameter>
 </method>
+<method name="endViewTransition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
 <method name="focusSearch"
  return="android.view.View"
  abstract="false"
@@ -203528,6 +203541,19 @@
  visibility="public"
 >
 </method>
+<method name="startViewTransition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
 <method name="updateViewLayout"
  return="void"
  abstract="false"
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 54a8e4b..1e2bbcc 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -686,6 +686,13 @@
     private void start(boolean playBackwards) {
         mPlayingBackwards = playBackwards;
         if ((mStartDelay == 0) && (Thread.currentThread() == Looper.getMainLooper().getThread())) {
+            if (mListeners != null) {
+                ArrayList<AnimatorListener> tmpListeners =
+                        (ArrayList<AnimatorListener>) mListeners.clone();
+                for (AnimatorListener listener : tmpListeners) {
+                    listener.onAnimationStart(this);
+                }
+            }
             // This sets the initial value of the animation, prior to actually starting it running
             setCurrentPlayTime(getCurrentPlayTime());
         }
@@ -783,7 +790,9 @@
     private void startAnimation() {
         initAnimation();
         sAnimations.add(this);
-        if (mListeners != null) {
+        if (mStartDelay > 0 && mListeners != null) {
+            // Listeners were already notified in start() if startDelay is 0; this is
+            // just for delayed animations
             ArrayList<AnimatorListener> tmpListeners =
                     (ArrayList<AnimatorListener>) mListeners.clone();
             for (AnimatorListener listener : tmpListeners) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 4d4f892..85a9d60 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -18,8 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.res.TypedArray;
 import android.os.Bundle;
 import android.os.Handler;
@@ -363,11 +362,7 @@
                                     Animator anim = loadAnimator(f, transit, true,
                                             transitionStyle);
                                     if (anim != null) {
-                                        if (anim instanceof AnimatorSet) {
-                                            ((AnimatorSet)anim).setTarget(f.mView);
-                                        } else if (anim instanceof ObjectAnimator) {
-                                            ((ObjectAnimator)anim).setTarget(f.mView);
-                                        }
+                                        anim.setTarget(f.mView);
                                         anim.start();
                                     }
                                     container.addView(f.mView);
@@ -447,17 +442,24 @@
                                     + " did not call through to super.onDestroyedView()");
                         }
                         if (f.mView != null && f.mContainer != null) {
+                            Animator anim = null;
                             if (mCurState > Fragment.INITIALIZING) {
-                                Animator anim = loadAnimator(f, transit, true,
+                                anim = loadAnimator(f, transit, false,
                                         transitionStyle);
-                                if (anim != null) {
-                                    if (anim instanceof AnimatorSet) {
-                                        ((AnimatorSet)anim).setTarget(f.mView);
-                                    } else if (anim instanceof ObjectAnimator) {
-                                        ((ObjectAnimator)anim).setTarget(f.mView);
+                            }
+                            if (anim != null) {
+                                final ViewGroup container = f.mContainer;
+                                final View view = f.mView;
+                                container.startViewTransition(view);
+                                anim.addListener(new AnimatorListenerAdapter() {
+                                    @Override
+                                    public void onAnimationEnd(Animator anim) {
+                                        container.endViewTransition(view);
                                     }
-                                    anim.start();
-                                }
+                                });
+                                anim.setTarget(f.mView);
+                                anim.start();
+
                             }
                             f.mContainer.removeView(f.mView);
                         }
@@ -591,11 +593,7 @@
                 Animator anim = loadAnimator(fragment, transition, true,
                         transitionStyle);
                 if (anim != null) {
-                    if (anim instanceof AnimatorSet) {
-                        ((AnimatorSet)anim).setTarget(fragment.mView);
-                    } else if (anim instanceof ObjectAnimator) {
-                        ((ObjectAnimator)anim).setTarget(fragment.mView);
-                    }
+                    anim.setTarget(fragment.mView);
                     anim.start();
                 }
                 fragment.mView.setVisibility(View.GONE);
@@ -615,11 +613,7 @@
                 Animator anim = loadAnimator(fragment, transition, true,
                         transitionStyle);
                 if (anim != null) {
-                    if (anim instanceof AnimatorSet) {
-                        ((AnimatorSet)anim).setTarget(fragment.mView);
-                    } else if (anim instanceof ObjectAnimator) {
-                        ((ObjectAnimator)anim).setTarget(fragment.mView);
-                    }
+                    anim.setTarget(fragment.mView);
                     anim.start();
                 }
                 fragment.mView.setVisibility(View.VISIBLE);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c8b26ef..3db05b6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2603,6 +2603,9 @@
      * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
      */
     public void setLayoutTransition(LayoutTransition transition) {
+        if (mTransition != null) {
+            mTransition.removeTransitionListener(mLayoutTransitionListener);
+        }
         mTransition = transition;
         if (mTransition != null) {
             mTransition.addTransitionListener(mLayoutTransitionListener);
@@ -3731,6 +3734,54 @@
         }
     }
 
+    /**
+     * This method tells the ViewGroup that the given View object, which should have this
+     * ViewGroup as its parent,
+     * should be kept around  (re-displayed when the ViewGroup draws its children) even if it
+     * is removed from its parent. This allows animations, such as those used by
+     * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
+     * the removal of views. A call to this method should always be accompanied by a later call
+     * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
+     * so that the View finally gets removed.
+     *
+     * @param view The View object to be kept visible even if it gets removed from its parent.
+     */
+    public void startViewTransition(View view) {
+        if (view.mParent == this) {
+            if (mTransitioningViews == null) {
+                mTransitioningViews = new ArrayList<View>();
+            }
+            mTransitioningViews.add(view);
+        }
+    }
+
+    /**
+     * This method should always be called following an earlier call to
+     * {@link #startViewTransition(View)}. The given View is finally removed from its parent
+     * and will no longer be displayed. Note that this method does not perform the functionality
+     * of removing a view from its parent; it just discontinues the display of a View that
+     * has previously been removed.
+     *
+     * @return view The View object that has been removed but is being kept around in the visible
+     * hierarchy by an earlier call to {@link #startViewTransition(View)}.
+     */
+    public void endViewTransition(View view) {
+        if (mTransitioningViews != null) {
+            mTransitioningViews.remove(view);
+            final ArrayList<View> disappearingChildren = mDisappearingChildren;
+            if (disappearingChildren != null && disappearingChildren.contains(view)) {
+                disappearingChildren.remove(view);
+                if (view.mAttachInfo != null) {
+                    view.dispatchDetachedFromWindow();
+                }
+                if (view.mParent != null) {
+                    view.mParent = null;
+                }
+                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+            }
+        }
+    }
+
     private LayoutTransition.TransitionListener mLayoutTransitionListener =
             new LayoutTransition.TransitionListener() {
         @Override
@@ -3739,10 +3790,7 @@
             // We only care about disappearing items, since we need special logic to keep
             // those items visible after they've been 'removed'
             if (transitionType == LayoutTransition.DISAPPEARING) {
-                if (mTransitioningViews == null) {
-                    mTransitioningViews = new ArrayList<View>();
-                }
-                mTransitioningViews.add(view);
+                startViewTransition(view);
             }
         }
 
@@ -3750,18 +3798,7 @@
         public void endTransition(LayoutTransition transition, ViewGroup container,
                 View view, int transitionType) {
             if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
-                mTransitioningViews.remove(view);
-                final ArrayList<View> disappearingChildren = mDisappearingChildren;
-                if (disappearingChildren != null && disappearingChildren.contains(view)) {
-                    disappearingChildren.remove(view);
-                    if (view.mAttachInfo != null) {
-                        view.dispatchDetachedFromWindow();
-                    }
-                    if (view.mParent != null) {
-                        view.mParent = null;
-                    }
-                    mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
-                }
+                endViewTransition(view);
             }
         }
     };