Fix issue #5367164: memory leak in LayoutTransition

When a transition occurs, layout change listeners are added to the container
being transitioned as well as every container up the view hierarchy. The
parent views were not having those listeners removed, so every time a transition
ran, more listeners would be added. Adding to that, the use of an ArrayList
as the collection to hold the listeners meant that adding duplicate items
would just increase the size of the list. There's now a sanity-check on the add
call to make sure that the listener does not exist already, but more importantly
we remove all listeners added when the transition ends.

Change-Id: I4ea05adf30765db091124065539b0ffd32729b3b
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 355b1fc..f383af9 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -24,6 +24,7 @@
 import android.view.animation.DecelerateInterpolator;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -615,10 +616,13 @@
         observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
             public boolean onPreDraw() {
                 parent.getViewTreeObserver().removeOnPreDrawListener(this);
-                int numChildren = parent.getChildCount();
-                for (int i = 0; i < numChildren; ++i) {
-                    final View child = parent.getChildAt(i);
-                    child.removeOnLayoutChangeListener(layoutChangeListenerMap.get(child));
+                int count = layoutChangeListenerMap.size();
+                if (count > 0) {
+                    Collection<View> views = layoutChangeListenerMap.keySet();
+                    for (View view : views) {
+                        View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
+                        view.removeOnLayoutChangeListener(listener);
+                    }
                 }
                 layoutChangeListenerMap.clear();
                 return true;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8e5aefd..632ab4a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3335,7 +3335,9 @@
         if (mOnLayoutChangeListeners == null) {
             mOnLayoutChangeListeners = new ArrayList<OnLayoutChangeListener>();
         }
-        mOnLayoutChangeListeners.add(listener);
+        if (!mOnLayoutChangeListeners.contains(listener)) {
+            mOnLayoutChangeListeners.add(listener);
+        }
     }
 
     /**