Introducing Fragment.

Basic implementation of an API for organizing a single activity into separate,
discrete pieces.  Currently supports adding and removing fragments, and
performing basic lifecycle callbacks on them.

Change-Id: I6ea8e6bdb04d93f8105c2e983fe9b6532422de34
diff --git a/api/current.xml b/api/current.xml
index edf9787..db75010 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -20304,6 +20304,17 @@
 <parameter name="view" type="android.view.View">
 </parameter>
 </method>
+<method name="openFragmentTransaction"
+ return="android.app.FragmentTransaction"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="openOptionsMenu"
  return="void"
  abstract="false"
@@ -24658,6 +24669,300 @@
 </parameter>
 </method>
 </class>
+<class name="Fragment"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.content.ComponentCallbacks">
+</implements>
+<constructor name="Fragment"
+ type="android.app.Fragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="Fragment"
+ type="android.app.Fragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</constructor>
+<method name="getActivity"
+ return="android.app.Activity"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onAttach"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+</method>
+<method name="onConfigurationChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="newConfig" type="android.content.res.Configuration">
+</parameter>
+</method>
+<method name="onCreate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onCreateView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="inflater" type="android.view.LayoutInflater">
+</parameter>
+<parameter name="container" type="android.view.ViewGroup">
+</parameter>
+</method>
+<method name="onDestroy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDetach"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onLowMemory"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onPause"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRestart"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRestoreInstanceState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onResume"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRetainNonConfigurationInstance"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onSaveInstanceState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="outState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onStart"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onStop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<interface name="FragmentTransaction"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="add"
+ 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>
+<parameter name="containerViewId" type="int">
+</parameter>
+</method>
+<method name="add"
+ 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>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="containerViewId" type="int">
+</parameter>
+</method>
+<method name="commit"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="remove"
+ 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>
+</interface>
 <class name="Instrumentation"
  extends="java.lang.Object"
  abstract="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a962391..15bf242 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -650,6 +650,65 @@
     private CharSequence mTitle;
     private int mTitleColor = 0;
 
+    final FragmentManager mFragments = new FragmentManager();
+    
+    private final class FragmentTransactionImpl implements FragmentTransaction {
+        ArrayList<Fragment> mAdded;
+        ArrayList<Fragment> mRemoved;
+        
+        public FragmentTransaction add(Fragment fragment, int containerViewId) {
+            return add(fragment, null, containerViewId);
+        }
+
+        public FragmentTransaction add(Fragment fragment, String name, int containerViewId) {
+            if (fragment.mActivity != null) {
+                throw new IllegalStateException("Fragment already added: " + fragment);
+            }
+            if (name != null) {
+                fragment.mName = name;
+            }
+            if (mRemoved != null) {
+                mRemoved.remove(fragment);
+            }
+            if (mAdded == null) {
+                mAdded = new ArrayList<Fragment>();
+            }
+            fragment.mContainerId = containerViewId;
+            mAdded.add(fragment);
+            return this;
+        }
+
+        public FragmentTransaction remove(Fragment fragment) {
+            if (fragment.mActivity == null) {
+                throw new IllegalStateException("Fragment not added: " + fragment);
+            }
+            if (mAdded != null) {
+                mAdded.remove(fragment);
+            }
+            if (mRemoved == null) {
+                mRemoved = new ArrayList<Fragment>();
+            }
+            mRemoved.add(fragment);
+            return this;
+        }
+
+        public void commit() {
+            if (mRemoved != null) {
+                for (int i=mRemoved.size()-1; i>=0; i--) {
+                    mFragments.removeFragment(mRemoved.get(i));
+                }
+            }
+            if (mAdded != null) {
+                for (int i=mAdded.size()-1; i>=0; i--) {
+                    mFragments.addFragment(mAdded.get(i));
+                }
+            }
+            if (mFragments != null) {
+                mFragments.moveToState(mFragments.mCurState);
+            }
+        }
+    }
+    
     private static final class ManagedCursor {
         ManagedCursor(Cursor cursor) {
             mCursor = cursor;
@@ -1464,6 +1523,14 @@
     }
     
     /**
+     * Start a series of edit operations on the Fragments associated with
+     * this activity.
+     */
+    public FragmentTransaction openFragmentTransaction() {
+        return new FragmentTransactionImpl();
+    }
+    
+    /**
      * Wrapper around
      * {@link ContentResolver#query(android.net.Uri , String[], String, String[], String)}
      * that gives the resulting {@link Cursor} to call
@@ -3743,6 +3810,8 @@
             Configuration config) {
         attachBaseContext(context);
 
+        mFragments.attachActivity(this);
+        
         mWindow = PolicyManager.makeNewWindow(this);
         mWindow.setCallback(this);
         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
@@ -3776,6 +3845,11 @@
         return mParent != null ? mParent.getActivityToken() : mToken;
     }
 
+    final void performCreate(Bundle icicle) {
+        onCreate(icicle);
+        mFragments.dispatchCreate(icicle);
+    }
+    
     final void performStart() {
         mCalled = false;
         mInstrumentation.callActivityOnStart(this);
@@ -3784,6 +3858,7 @@
                 "Activity " + mComponent.toShortString() +
                 " did not call through to super.onStart()");
         }
+        mFragments.dispatchStart();
     }
     
     final void performRestart() {
@@ -3830,6 +3905,9 @@
         // Now really resume, and install the current status bar and menu.
         mResumed = true;
         mCalled = false;
+        
+        mFragments.dispatchResume();
+        
         onPostResume();
         if (!mCalled) {
             throw new SuperNotCalledException(
@@ -3839,6 +3917,7 @@
     }
 
     final void performPause() {
+        mFragments.dispatchPause();
         onPause();
     }
     
@@ -3853,6 +3932,8 @@
                 mWindow.closeAllPanels();
             }
 
+            mFragments.dispatchStop();
+            
             mCalled = false;
             mInstrumentation.callActivityOnStop(this);
             if (!mCalled) {
@@ -3877,6 +3958,11 @@
         mResumed = false;
     }
 
+    final void performDestroy() {
+        mFragments.dispatchDestroy();
+        onDestroy();
+    }
+    
     final boolean isResumed() {
         return mResumed;
     }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5468d52..87b77d65 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3643,7 +3643,7 @@
             }
             try {
                 r.activity.mCalled = false;
-                r.activity.onDestroy();
+                mInstrumentation.callActivityOnDestroy(r.activity);
                 if (!r.activity.mCalled) {
                     throw new SuperNotCalledException(
                         "Activity " + safeToComponentShortString(r.intent) +
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
new file mode 100644
index 0000000..c0dc869
--- /dev/null
+++ b/core/java/android/app/Fragment.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.ComponentCallbacks;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A Fragment is a piece of an application's user interface or behavior
+ * that can be placed in an {@link Activity}.
+ */
+public class Fragment implements ComponentCallbacks {
+    static final int INITIALIZING = 0;  // Not yet created.
+    static final int CREATED = 1;       // Created.
+    static final int STARTED = 2;       // Created and started, not resumed.
+    static final int RESUMED = 3;       // Created started and resumed.
+    
+    String mName;
+    
+    int mState = INITIALIZING;
+    Activity mActivity;
+    
+    boolean mCalled;
+    int mContainerId;
+    
+    ViewGroup mContainer;
+    View mView;
+    
+    public Fragment() {
+    }
+    
+    public Fragment(String name) {
+        mName = name;
+    }
+    
+    public String getName() {
+        return mName;
+    }
+    
+    public Activity getActivity() {
+        return mActivity;
+    }
+    
+    public void onAttach(Activity activity) {
+        mCalled = true;
+    }
+    
+    public void onCreate(Bundle savedInstanceState) {
+        mCalled = true;
+    }
+    
+    public View onCreateView(LayoutInflater inflater, ViewGroup container) {
+        return null;
+    }
+    
+    public void onRestoreInstanceState(Bundle savedInstanceState) {
+    }
+    
+    public void onStart() {
+        mCalled = true;
+    }
+    
+    public void onRestart() {
+        mCalled = true;
+    }
+    
+    public void onResume() {
+        mCalled = true;
+    }
+    
+    public void onSaveInstanceState(Bundle outState) {
+    }
+    
+    public void onConfigurationChanged(Configuration newConfig) {
+        mCalled = true;
+    }
+    
+    public Object onRetainNonConfigurationInstance() {
+        return null;
+    }
+    
+    public void onPause() {
+        mCalled = true;
+    }
+    
+    public void onStop() {
+        mCalled = true;
+    }
+    
+    public void onLowMemory() {
+        mCalled = true;
+    }
+    
+    public void onDestroy() {
+        mCalled = true;
+    }
+    
+    public void onDetach() {
+        mCalled = true;
+    }
+}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
new file mode 100644
index 0000000..d5e49cf
--- /dev/null
+++ b/core/java/android/app/FragmentManager.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Bundle;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Container for fragments associated with an activity.
+ */
+class FragmentManager {
+    final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
+    
+    int mCurState = Fragment.INITIALIZING;
+    Activity mActivity;
+    
+    void moveToState(Fragment f, int newState) {
+        if (f.mState < newState) {
+            switch (f.mState) {
+                case Fragment.INITIALIZING:
+                    f.mActivity = mActivity;
+                    f.mCalled = false;
+                    f.onAttach(mActivity);
+                    if (!f.mCalled) {
+                        throw new SuperNotCalledException("Fragment " + f
+                                + " did not call through to super.onAttach()");
+                    }
+                    f.mCalled = false;
+                    f.onCreate(null);
+                    if (!f.mCalled) {
+                        throw new SuperNotCalledException("Fragment " + f
+                                + " did not call through to super.onCreate()");
+                    }
+                    
+                    ViewGroup container = null;
+                    if (f.mContainerId != 0) {
+                        container = (ViewGroup)mActivity.findViewById(f.mContainerId);
+                        if (container == null) {
+                            throw new IllegalArgumentException("New view found for id 0x"
+                                    + Integer.toHexString(f.mContainerId)
+                                    + " for fragment " + f);
+                        }
+                    }
+                    f.mContainer = container;
+                    f.mView = f.onCreateView(mActivity.getLayoutInflater(), container);
+                    if (container != null && f.mView != null) {
+                        container.addView(f.mView);
+                    }
+                    
+                case Fragment.CREATED:
+                    if (newState > Fragment.CREATED) {
+                        f.mCalled = false;
+                        f.onStart();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onStart()");
+                        }
+                    }
+                case Fragment.STARTED:
+                    if (newState > Fragment.STARTED) {
+                        f.mCalled = false;
+                        f.onResume();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onResume()");
+                        }
+                    }
+            }
+        } else if (f.mState > newState) {
+            switch (f.mState) {
+                case Fragment.RESUMED:
+                    if (newState < Fragment.RESUMED) {
+                        f.mCalled = false;
+                        f.onPause();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onPause()");
+                        }
+                    }
+                case Fragment.STARTED:
+                    if (newState < Fragment.STARTED) {
+                        f.mCalled = false;
+                        f.onStop();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onStop()");
+                        }
+                    }
+                case Fragment.CREATED:
+                    if (newState < Fragment.CREATED) {
+                        if (f.mContainer != null && f.mView != null) {
+                            f.mContainer.removeView(f.mView);
+                        }
+                        f.mContainer = null;
+                        f.mView = null;
+                        
+                        f.mCalled = false;
+                        f.onDestroy();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onDestroy()");
+                        }
+                        f.mCalled = false;
+                        f.onDetach();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onDetach()");
+                        }
+                        f.mActivity = null;
+                    }
+            }
+        }
+        
+        f.mState = newState;
+    }
+    
+    void moveToState(int newState) {
+        if (mActivity == null && newState != Fragment.INITIALIZING) {
+            throw new IllegalStateException("No activity");
+        }
+        
+        mCurState = newState;
+        for (int i=0; i<mFragments.size(); i++) {
+            Fragment f = mFragments.get(i);
+            moveToState(f, newState);
+        }
+    }
+    
+    public void addFragment(Fragment fragment) {
+        mFragments.add(fragment);
+    }
+    
+    public void removeFragment(Fragment fragment) {
+        mFragments.remove(fragment);
+        moveToState(fragment, Fragment.INITIALIZING);
+    }
+    
+    public void attachActivity(Activity activity) {
+        if (mActivity != null) throw new IllegalStateException();
+        mActivity = activity;
+    }
+    
+    public void dispatchCreate(Bundle state) {
+        moveToState(Fragment.CREATED);
+    }
+    
+    public void dispatchStart() {
+        moveToState(Fragment.STARTED);
+    }
+    
+    public void dispatchResume() {
+        moveToState(Fragment.RESUMED);
+    }
+    
+    public void dispatchPause() {
+        moveToState(Fragment.STARTED);
+    }
+    
+    public void dispatchStop() {
+        moveToState(Fragment.CREATED);
+    }
+    
+    public void dispatchDestroy() {
+        moveToState(Fragment.INITIALIZING);
+        mActivity = null;
+    }
+}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
new file mode 100644
index 0000000..f97e510
--- /dev/null
+++ b/core/java/android/app/FragmentTransaction.java
@@ -0,0 +1,11 @@
+package android.app;
+
+/**
+ * API for performing a set of Fragment operations.
+ */
+public interface FragmentTransaction {
+    public FragmentTransaction add(Fragment fragment, int containerViewId);
+    public FragmentTransaction add(Fragment fragment, String name, int containerViewId);
+    public FragmentTransaction remove(Fragment fragment);
+    public void commit();
+}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b8c3aa3..7ed7c49 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1072,7 +1072,7 @@
           }
       }
       
-      activity.onDestroy();
+      activity.performDestroy();
       
       if (mActivityMonitors != null) {
           synchronized (mSync) {