Fix IOOBE due to internal list being updated from remove

This CL removes all prepareChildren(), leaving
only the one in onMeasure(). This makes the state
more stable since we will only update it within a
layout pass. This also reduces any unnecessary work.

BUG: 30252077
Change-Id: Ibd00a3c3ede1dd654b0ed6d1d8d8d1c8da94e57a
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index 201ed9b..268dbe3 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -624,13 +624,7 @@
         return result;
     }
 
-    private void prepareChildren(final boolean forceRefresh) {
-        if (!forceRefresh && mChildDag.size() == getChildCount()
-                && mChildDag.size() == mDependencySortedChildren.size()) {
-            // If we're not being forced and everything looks good, lets skip the call
-            return;
-        }
-
+    private void prepareChildren() {
         mDependencySortedChildren.clear();
         mChildDag.clear();
 
@@ -709,7 +703,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        prepareChildren(true);
+        prepareChildren();
         ensurePreDrawListener();
 
         final int paddingLeft = getPaddingLeft();
@@ -1395,12 +1389,10 @@
      * @param view the View to find dependents of to dispatch the call.
      */
     public void dispatchDependentViewsChanged(View view) {
-        prepareChildren(false);
-
         final List<View> dependents = mChildDag.getIncomingEdges(view);
         if (dependents != null && !dependents.isEmpty()) {
             for (int i = 0; i < dependents.size(); i++) {
-                final View child = (View) dependents.get(i);
+                final View child = dependents.get(i);
                 CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)
                         child.getLayoutParams();
                 CoordinatorLayout.Behavior b = lp.getBehavior();
@@ -1421,8 +1413,6 @@
      */
     @NonNull
     public List<View> getDependencies(@NonNull View child) {
-        prepareChildren(false);
-
         final List<View> dependencies = mChildDag.getOutgoingEdges(child);
         mTempDependenciesList.clear();
         if (dependencies != null) {
@@ -1441,8 +1431,6 @@
      */
     @NonNull
     public List<View> getDependents(@NonNull View child) {
-        prepareChildren(false);
-
         final List<View> edges = mChildDag.getIncomingEdges(child);
         mTempDependenciesList.clear();
         if (edges != null) {
@@ -1453,7 +1441,7 @@
 
     @VisibleForTesting
     final List<View> getDependencySortedChildren() {
-        prepareChildren(true);
+        prepareChildren();
         return Collections.unmodifiableList(mDependencySortedChildren);
     }
 
@@ -1483,8 +1471,7 @@
     /**
      * Check if the given child has any layout dependencies on other child views.
      */
-    boolean hasDependencies(View child) {
-        prepareChildren(false);
+    private boolean hasDependencies(View child) {
         return mChildDag.hasOutgoingEdges(child);
     }
 
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
index 5471c38..f6350c1 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
@@ -293,4 +293,45 @@
         // And assert that View B's Behavior was called appropriately
         verify(behavior, times(1)).onDependentViewRemoved(col, viewB, viewA);
     }
+
+    @Test
+    public void testGetDependenciesAfterDependentViewRemoved() {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
+
+        // Add two views, A & B, where B depends on A
+        final View viewA = new View(col.getContext());
+        final View viewB = new View(col.getContext());
+        final CoordinatorLayout.LayoutParams lpB = col.generateDefaultLayoutParams();
+        final CoordinatorLayout.Behavior behavior
+                = new CoordinatorLayoutUtils.DependentBehavior(viewA) {
+            @Override
+            public void onDependentViewRemoved(CoordinatorLayout parent, View child,
+                    View dependency) {
+                parent.getDependencies(child);
+            }
+        };
+        lpB.setBehavior(behavior);
+
+        // Now add views
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                col.addView(viewA);
+                col.addView(viewB, lpB);
+            }
+        });
+
+        // Wait for a layout
+        instrumentation.waitForIdleSync();
+
+        // Now remove view A, which will trigger onDependentViewRemoved() on view B's behavior
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                col.removeView(viewA);
+            }
+        });
+    }
+
 }