Protect use of onFindViewById for Fragment Transitions.

Bug 34163850

onFindViewById is only valid once the Fragment's View
has been created, but the child fragment manager
can be used prior to its creation. This protects
the use of onFindViewById from use by fragment
transitions.

Test: gradlew connectedCheck in fragments/

Change-Id: I31c1c30dc721ce95cf6d0f11cbb2cf0b34649a36
diff --git a/fragment/java/android/support/v4/app/FragmentTransition.java b/fragment/java/android/support/v4/app/FragmentTransition.java
index 71ac889..5e0d9e3 100644
--- a/fragment/java/android/support/v4/app/FragmentTransition.java
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -186,7 +186,10 @@
     private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
-        ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        ViewGroup sceneRoot = null;
+        if (fragmentManager.mContainer.onHasView()) {
+            sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        }
         if (sceneRoot == null) {
             return;
         }
@@ -276,7 +279,10 @@
     private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
-        ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        ViewGroup sceneRoot = null;
+        if (fragmentManager.mContainer.onHasView()) {
+            sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        }
         if (sceneRoot == null) {
             return;
         }
diff --git a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
index 296c3f8..521eb84 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
@@ -32,6 +32,7 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -942,6 +943,7 @@
     // Ensure that non-optimized transactions are executed individually rather than together.
     // This forces references from one fragment to another that should be executed earlier
     // to work.
+    @Test
     public void nonOptimizeTogether() throws Throwable {
         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
         ViewGroup container = (ViewGroup)
@@ -973,6 +975,33 @@
         assertNotNull(findViewById(R.id.textA));
     }
 
+    // Ensure that there is no problem if the child fragment manager is used before
+    // the View has been added.
+    @Test
+    public void childFragmentManager() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final StrictViewFragment fragment1 = new ParentFragment();
+        fragment1.setLayoutId(R.layout.double_container);
+
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment1);
+        ViewGroup innerContainer = (ViewGroup)
+                fragment1.getView().findViewById(R.id.fragmentContainer1);
+
+        Fragment fragment2 = fragment1.getChildFragmentManager().findFragmentByTag("inner");
+        FragmentTestUtil.assertChildren(innerContainer, fragment2);
+    }
+
     private View findViewById(int viewId) {
         return mActivityRule.getActivity().findViewById(viewId);
     }
@@ -996,4 +1025,25 @@
             super.onViewCreated(view, savedInstanceState);
         }
     }
+
+    public static class ParentFragment extends StrictViewFragment {
+        public ParentFragment() {
+            setLayoutId(R.layout.double_container);
+        }
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                                 Bundle savedInstanceState) {
+            View view = super.onCreateView(inflater, container, savedInstanceState);
+            final StrictViewFragment fragment2 = new StrictViewFragment();
+            fragment2.setLayoutId(R.layout.fragment_a);
+
+            getChildFragmentManager().beginTransaction()
+                    .add(R.id.fragmentContainer1, fragment2, "inner")
+                    .addToBackStack(null)
+                    .commit();
+            getChildFragmentManager().executePendingTransactions();
+            return view;
+        }
+    }
 }