Merge "DO NOT MERGE.  Integrate fragment work from master" into honeycomb-mr2
diff --git a/api/current.xml b/api/current.xml
index b2330be..3843f67 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -30529,6 +30529,21 @@
  visibility="public"
 >
 </method>
+<method name="onViewCreated"
+ 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>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
 <method name="registerForContextMenu"
  return="void"
  abstract="false"
@@ -31262,6 +31277,19 @@
 <parameter name="name" type="java.lang.String">
 </parameter>
 </method>
+<method name="attach"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
 <method name="commit"
  return="int"
  abstract="true"
@@ -31284,6 +31312,19 @@
  visibility="public"
 >
 </method>
+<method name="detach"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
 <method name="disallowAddToBackStack"
  return="android.app.FragmentTransaction"
  abstract="true"
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 1d217f0..09e3d76 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -169,6 +169,8 @@
     static final int OP_REMOVE = 3;
     static final int OP_HIDE = 4;
     static final int OP_SHOW = 5;
+    static final int OP_DETACH = 6;
+    static final int OP_ATTACH = 7;
 
     static final class Op {
         Op next;
@@ -401,6 +403,32 @@
         return this;
     }
 
+    public FragmentTransaction detach(Fragment fragment) {
+        //if (fragment.mImmediateActivity == null) {
+        //    throw new IllegalStateException("Fragment not added: " + fragment);
+        //}
+
+        Op op = new Op();
+        op.cmd = OP_DETACH;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
+    public FragmentTransaction attach(Fragment fragment) {
+        //if (fragment.mImmediateActivity == null) {
+        //    throw new IllegalStateException("Fragment not added: " + fragment);
+        //}
+
+        Op op = new Op();
+        op.cmd = OP_ATTACH;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
     public FragmentTransaction setCustomAnimations(int enter, int exit) {
         mEnterAnim = enter;
         mExitAnim = exit;
@@ -567,6 +595,16 @@
                     f.mNextAnim = op.enterAnim;
                     mManager.showFragment(f, mTransition, mTransitionStyle);
                 } break;
+                case OP_DETACH: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.exitAnim;
+                    mManager.detachFragment(f, mTransition, mTransitionStyle);
+                } break;
+                case OP_ATTACH: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.enterAnim;
+                    mManager.attachFragment(f, mTransition, mTransitionStyle);
+                } break;
                 default: {
                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                 }
@@ -627,6 +665,16 @@
                     mManager.hideFragment(f,
                             FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
                 } break;
+                case OP_DETACH: {
+                    Fragment f = op.fragment;
+                    mManager.attachFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+                } break;
+                case OP_ATTACH: {
+                    Fragment f = op.fragment;
+                    mManager.detachFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+                } break;
                 default: {
                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                 }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 53dc7c8..dd158f9 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -52,6 +52,7 @@
     final int mContainerId;
     final String mTag;
     final boolean mRetainInstance;
+    final boolean mDetached;
     final Bundle mArguments;
     
     Bundle mSavedFragmentState;
@@ -66,6 +67,7 @@
         mContainerId = frag.mContainerId;
         mTag = frag.mTag;
         mRetainInstance = frag.mRetainInstance;
+        mDetached = frag.mDetached;
         mArguments = frag.mArguments;
     }
     
@@ -77,6 +79,7 @@
         mContainerId = in.readInt();
         mTag = in.readString();
         mRetainInstance = in.readInt() != 0;
+        mDetached = in.readInt() != 0;
         mArguments = in.readBundle();
         mSavedFragmentState = in.readBundle();
     }
@@ -103,6 +106,7 @@
         mInstance.mContainerId = mContainerId;
         mInstance.mTag = mTag;
         mInstance.mRetainInstance = mRetainInstance;
+        mInstance.mDetached = mDetached;
         mInstance.mFragmentManager = activity.mFragments;
         
         return mInstance;
@@ -120,6 +124,7 @@
         dest.writeInt(mContainerId);
         dest.writeString(mTag);
         dest.writeInt(mRetainInstance ? 1 : 0);
+        dest.writeInt(mDetached ? 1 : 0);
         dest.writeBundle(mArguments);
         dest.writeBundle(mSavedFragmentState);
     }
@@ -321,8 +326,9 @@
     static final int INITIALIZING = 0;     // Not yet created.
     static final int CREATED = 1;          // Created.
     static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
-    static final int STARTED = 3;          // Created and started, not resumed.
-    static final int RESUMED = 4;          // Created started and resumed.
+    static final int STOPPED = 3;          // Fully created, not started.
+    static final int STARTED = 4;          // Created and started, not resumed.
+    static final int RESUMED = 5;          // Created started and resumed.
     
     int mState = INITIALIZING;
     
@@ -404,6 +410,9 @@
     // from the user.
     boolean mHidden;
     
+    // Set to true when the app has requested that this fragment be detached.
+    boolean mDetached;
+
     // If set this fragment would like its instance retained across
     // configuration changes.
     boolean mRetainInstance;
@@ -511,23 +520,27 @@
         }
     }
     
-    void restoreViewState() {
+    final void restoreViewState() {
         if (mSavedViewState != null) {
             mView.restoreHierarchyState(mSavedViewState);
             mSavedViewState = null;
         }
     }
     
-    void setIndex(int index) {
+    final void setIndex(int index) {
         mIndex = index;
         mWho = "android:fragment:" + mIndex;
    }
     
-    void clearIndex() {
+    final void clearIndex() {
         mIndex = -1;
         mWho = null;
     }
     
+    final boolean isInBackStack() {
+        return mBackStackNesting > 0;
+    }
+
     /**
      * Subclasses can not override equals().
      */
@@ -947,6 +960,19 @@
     }
     
     /**
+     * Called immediately after {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}
+     * has returned, but before any saved state has been restored in to the view.
+     * This gives subclasses a chance to initialize themselves once
+     * they know their view hierarchy has been completely created.  The fragment's
+     * view hierarchy is not however attached to its parent at this point.
+     * @param view The View returned by {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+     * @param savedInstanceState If non-null, this fragment is being re-constructed
+     * from a previous saved state as given here.
+     */
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+    }
+    
+    /**
      * Called to have the fragment instantiate its user interface view.
      * This is optional, and non-graphical fragments can return null (which
      * is the default implementation).  This will be called between
@@ -1280,6 +1306,7 @@
                 writer.print(" mFromLayout="); writer.print(mFromLayout);
                 writer.print(" mInLayout="); writer.println(mInLayout);
         writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
+                writer.print(" mDetached="); writer.print(mDetached);
                 writer.print(" mRetainInstance="); writer.print(mRetainInstance);
                 writer.print(" mRetaining="); writer.print(mRetaining);
                 writer.print(" mHasMenu="); writer.println(mHasMenu);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index ab60cf0..0da656f 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -714,13 +714,14 @@
                                 null, f.mSavedFragmentState);
                         if (f.mView != null) {
                             f.mView.setSaveFromParentEnabled(false);
+                            if (f.mHidden) f.mView.setVisibility(View.GONE);
                             f.restoreViewState();
-                            if (f.mHidden) f.mView.setVisibility(View.GONE); 
+                            f.onViewCreated(f.mView, f.mSavedFragmentState);
                         }
                     }
                 case Fragment.CREATED:
                     if (newState > Fragment.CREATED) {
-                        if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f);
+                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                         if (!f.mFromLayout) {
                             ViewGroup container = null;
                             if (f.mContainerId != 0) {
@@ -744,9 +745,10 @@
                                         anim.start();
                                     }
                                     container.addView(f.mView);
-                                    f.restoreViewState();
                                 }
-                                if (f.mHidden) f.mView.setVisibility(View.GONE); 
+                                if (f.mHidden) f.mView.setVisibility(View.GONE);
+                                f.restoreViewState();
+                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                             }
                         }
                         
@@ -756,10 +758,13 @@
                             throw new SuperNotCalledException("Fragment " + f
                                     + " did not call through to super.onActivityCreated()");
                         }
+                        if (f.mView != null) {
+                        }
                         f.mSavedFragmentState = null;
                     }
                 case Fragment.ACTIVITY_CREATED:
-                    if (newState > Fragment.ACTIVITY_CREATED) {
+                case Fragment.STOPPED:
+                    if (newState > Fragment.STOPPED) {
                         if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                         f.mCalled = false;
                         f.onStart();
@@ -803,9 +808,10 @@
                                     + " did not call through to super.onStop()");
                         }
                     }
+                case Fragment.STOPPED:
                 case Fragment.ACTIVITY_CREATED:
                     if (newState < Fragment.ACTIVITY_CREATED) {
-                        if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
+                        if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
                         if (f.mView != null) {
                             // Need to save the current view state if not
                             // done already.
@@ -971,32 +977,36 @@
         if (mAdded == null) {
             mAdded = new ArrayList<Fragment>();
         }
-        mAdded.add(fragment);
-        makeActive(fragment);
         if (DEBUG) Log.v(TAG, "add: " + fragment);
-        fragment.mAdded = true;
-        fragment.mRemoving = false;
-        if (fragment.mHasMenu) {
-            mNeedMenuInvalidate = true;
-        }
-        if (moveToStateNow) {
-            moveToState(fragment);
+        makeActive(fragment);
+        if (!fragment.mDetached) {
+            mAdded.add(fragment);
+            fragment.mAdded = true;
+            fragment.mRemoving = false;
+            if (fragment.mHasMenu) {
+                mNeedMenuInvalidate = true;
+            }
+            if (moveToStateNow) {
+                moveToState(fragment);
+            }
         }
     }
     
     public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
         if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
-        mAdded.remove(fragment);
-        final boolean inactive = fragment.mBackStackNesting <= 0;
-        if (fragment.mHasMenu) {
-            mNeedMenuInvalidate = true;
-        }
-        fragment.mAdded = false;
-        fragment.mRemoving = true;
-        moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
-                transition, transitionStyle);
-        if (inactive) {
-            makeInactive(fragment);
+        final boolean inactive = !fragment.isInBackStack();
+        if (!fragment.mDetached || inactive) {
+            mAdded.remove(fragment);
+            if (fragment.mHasMenu) {
+                mNeedMenuInvalidate = true;
+            }
+            fragment.mAdded = false;
+            fragment.mRemoving = true;
+            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
+                    transition, transitionStyle);
+            if (inactive) {
+                makeInactive(fragment);
+            }
         }
     }
     
@@ -1052,6 +1062,39 @@
         }
     }
     
+    public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "detach: " + fragment);
+        if (!fragment.mDetached) {
+            fragment.mDetached = true;
+            if (fragment.mAdded) {
+                // We are not already in back stack, so need to remove the fragment.
+                mAdded.remove(fragment);
+                if (fragment.mHasMenu) {
+                    mNeedMenuInvalidate = true;
+                }
+                fragment.mAdded = false;
+                fragment.mRemoving = true;
+                moveToState(fragment, Fragment.CREATED, transition, transitionStyle);
+            }
+        }
+    }
+
+    public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "attach: " + fragment);
+        if (fragment.mDetached) {
+            fragment.mDetached = false;
+            if (!fragment.mAdded) {
+                mAdded.add(fragment);
+                fragment.mAdded = true;
+                fragment.mRemoving = false;
+                if (fragment.mHasMenu) {
+                    mNeedMenuInvalidate = true;
+                }
+                moveToState(fragment, mCurState, transition, transitionStyle);
+            }
+        }
+    }
+
     public Fragment findFragmentById(int id) {
         if (mActive != null) {
             // First look through added fragments.
@@ -1594,7 +1637,7 @@
     }
     
     public void dispatchStop() {
-        moveToState(Fragment.ACTIVITY_CREATED, false);
+        moveToState(Fragment.STOPPED, false);
     }
     
     public void dispatchDestroy() {
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 0cc774d..15b873b 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -87,6 +87,31 @@
     public abstract FragmentTransaction show(Fragment fragment);
 
     /**
+     * Detach the given fragment from the UI.  This is the same state as
+     * when it is put on the back stack: the fragment is removed from
+     * the UI, however its state is still being actively managed by the
+     * fragment manager.  When going into this state its view hierarchy
+     * is destroyed.
+     *
+     * @param fragment The fragment to be detached.
+     *
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public abstract FragmentTransaction detach(Fragment fragment);
+
+    /**
+     * Re-attach a fragment after it had previously been deatched from
+     * the UI with {@link #detach(Fragment)}.  This
+     * causes its view hierarchy to be re-created, attached to the UI,
+     * and displayed.
+     *
+     * @param fragment The fragment to be attached.
+     *
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public abstract FragmentTransaction attach(Fragment fragment);
+
+    /**
      * @return <code>true</code> if this transaction contains no operations,
      * <code>false</code> otherwise.
      */
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
index 6e2f4b6..a5ee26c 100644
--- a/core/java/android/app/ListFragment.java
+++ b/core/java/android/app/ListFragment.java
@@ -195,11 +195,11 @@
     }
 
     /**
-     * Attach to list view once Fragment is ready to run.
+     * Attach to list view once the view hierarchy has been created.
      */
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         ensureList();
     }