summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.xml127
-rw-r--r--core/java/android/app/Activity.java289
-rw-r--r--core/java/android/app/ActivityThread.java29
-rw-r--r--core/java/android/app/Fragment.java318
-rw-r--r--core/java/android/app/FragmentManager.java588
-rw-r--r--core/java/android/app/FragmentTransaction.java2
-rw-r--r--core/java/android/app/Instrumentation.java6
-rw-r--r--core/java/android/app/LocalActivityManager.java13
-rw-r--r--core/java/android/view/View.java46
-rw-r--r--core/java/android/view/ViewGroup.java10
10 files changed, 1140 insertions, 288 deletions
diff --git a/api/current.xml b/api/current.xml
index 1eaa6f4a8961..26de53c13d98 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19904,6 +19904,19 @@
<parameter name="id" type="int">
</parameter>
</method>
+<method name="findFragmentByTag"
+ return="android.app.Fragment"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
<method name="findViewById"
return="android.view.View"
abstract="false"
@@ -25414,6 +25427,19 @@
visibility="public"
>
</constructor>
+<method name="equals"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="java.lang.Object">
+</parameter>
+</method>
<method name="getActivity"
return="android.app.Activity"
abstract="false"
@@ -25425,6 +25451,61 @@
visibility="public"
>
</method>
+<method name="getId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRetainInstance"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTag"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hashCode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onAttach"
return="void"
abstract="false"
@@ -25493,6 +25574,8 @@
</parameter>
<parameter name="container" type="android.view.ViewGroup">
</parameter>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
</method>
<method name="onDestroy"
return="void"
@@ -25553,7 +25636,7 @@
visibility="public"
>
</method>
-<method name="onRestoreInstanceState"
+<method name="onReady"
return="void"
abstract="false"
native="false"
@@ -25577,8 +25660,8 @@
visibility="public"
>
</method>
-<method name="onRetainNonConfigurationInstance"
- return="java.lang.Object"
+<method name="onSaveInstanceState"
+ return="void"
abstract="false"
native="false"
synchronized="false"
@@ -25587,8 +25670,10 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="outState" type="android.os.Bundle">
+</parameter>
</method>
-<method name="onSaveInstanceState"
+<method name="onStart"
return="void"
abstract="false"
native="false"
@@ -25598,10 +25683,8 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="outState" type="android.os.Bundle">
-</parameter>
</method>
-<method name="onStart"
+<method name="onStop"
return="void"
abstract="false"
native="false"
@@ -25612,7 +25695,7 @@
visibility="public"
>
</method>
-<method name="onStop"
+<method name="setRetainInstance"
return="void"
abstract="false"
native="false"
@@ -25622,6 +25705,8 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="retain" type="boolean">
+</parameter>
</method>
</class>
<interface name="FragmentTransaction"
@@ -25643,6 +25728,8 @@
>
<parameter name="fragment" type="android.app.Fragment">
</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
</method>
<method name="add"
return="android.app.FragmentTransaction"
@@ -181501,6 +181588,17 @@
visibility="public"
>
</method>
+<method name="isSaveFromParentEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isScrollbarFadingEnabled"
return="boolean"
abstract="false"
@@ -182979,6 +183077,19 @@
<parameter name="enabled" type="boolean">
</parameter>
</method>
+<method name="setSaveFromParentEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
<method name="setScrollBarStyle"
return="void"
abstract="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 21c6d2a49767..16f3265e6d39 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -42,6 +42,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -77,6 +78,9 @@ import android.widget.LinearLayout;
import com.android.internal.app.SplitActionBar;
import com.android.internal.policy.PolicyManager;
+import java.util.ArrayList;
+import java.util.HashMap;
+
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -611,6 +615,7 @@ public class Activity extends ContextThemeWrapper
private static long sInstanceCount = 0;
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
+ private static final String FRAGMENTS_TAG = "android:fragments";
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
@@ -632,8 +637,6 @@ public class Activity extends ContextThemeWrapper
private ComponentName mComponent;
/*package*/ ActivityInfo mActivityInfo;
/*package*/ ActivityThread mMainThread;
- /*package*/ Object mLastNonConfigurationInstance;
- /*package*/ HashMap<String,Object> mLastNonConfigurationChildInstances;
Activity mParent;
boolean mCalled;
private boolean mResumed;
@@ -646,6 +649,13 @@ public class Activity extends ContextThemeWrapper
/*package*/ Configuration mCurrentConfig;
private SearchManager mSearchManager;
+ static final class NonConfigurationInstances {
+ Object activity;
+ HashMap<String, Object> children;
+ ArrayList<Fragment> fragments;
+ }
+ /* package */ NonConfigurationInstances mLastNonConfigurationInstances;
+
private Window mWindow;
private WindowManager mWindowManager;
@@ -660,148 +670,6 @@ public class Activity extends ContextThemeWrapper
final FragmentManager mFragments = new FragmentManager();
- private final Object[] sConstructorArgs = new Object[0];
-
- private static final Class[] sConstructorSignature = new Class[] { };
-
- private static final HashMap<String, Constructor> sConstructorMap =
- new HashMap<String, Constructor>();
-
- private final class FragmentTransactionImpl implements FragmentTransaction,
- Runnable, BackStackState {
- ArrayList<Fragment> mAdded;
- ArrayList<Fragment> mRemoved;
- int mTransition;
- int mTransitionStyle;
- boolean mAddToBackStack;
- String mName;
- boolean mCommitted;
-
- public FragmentTransaction add(Fragment fragment) {
- return add(fragment, 0);
- }
-
- public FragmentTransaction add(Fragment fragment, int containerViewId) {
- if (fragment.mActivity != null) {
- throw new IllegalStateException("Fragment already added: " + fragment);
- }
- if (mRemoved != null) {
- mRemoved.remove(fragment);
- }
- if (mAdded == null) {
- mAdded = new ArrayList<Fragment>();
- }
- fragment.mContainerId = containerViewId;
- mAdded.add(fragment);
- return this;
- }
-
- public FragmentTransaction replace(Fragment fragment, int containerViewId) {
- if (containerViewId == 0) {
- throw new IllegalArgumentException("Must use non-zero containerViewId");
- }
- if (mFragments.mFragments != null) {
- for (int i=0; i<mFragments.mFragments.size(); i++) {
- Fragment old = mFragments.mFragments.get(i);
- if (old.mContainerId == containerViewId) {
- remove(old);
- }
- }
- }
- return add(fragment, containerViewId);
- }
-
- 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 FragmentTransaction setTransition(int transition) {
- mTransition = transition;
- return this;
- }
-
- public FragmentTransaction setTransitionStyle(int styleRes) {
- mTransitionStyle = styleRes;
- return this;
- }
-
- public FragmentTransaction addToBackStack(String name) {
- mAddToBackStack = true;
- mName = name;
- return this;
- }
-
- public void commit() {
- if (mCommitted) throw new IllegalStateException("commit already called");
- mCommitted = true;
- mHandler.post(this);
- }
-
- public void run() {
- if (mRemoved != null) {
- for (int i=mRemoved.size()-1; i>=0; i--) {
- mFragments.removeFragment(mRemoved.get(i), mTransition,
- mTransitionStyle);
- }
- }
- if (mAdded != null) {
- for (int i=mAdded.size()-1; i>=0; i--) {
- Fragment f = mAdded.get(i);
- mFragments.addFragment(f, false);
- if (mAddToBackStack) {
- f.mBackStackNesting++;
- }
- }
- }
- mFragments.moveToState(mFragments.mCurState, mTransition,
- mTransitionStyle, true);
- if (mAddToBackStack) {
- mFragments.addBackStackState(this);
- }
- }
-
- public void popFromBackStack() {
- if (mAdded != null) {
- for (int i=mAdded.size()-1; i>=0; i--) {
- Fragment f = mAdded.get(i);
- if (mAddToBackStack) {
- f.mBackStackNesting--;
- }
- mFragments.removeFragment(f,
- FragmentManager.reverseTransit(mTransition),
- mTransitionStyle);
- }
- }
- if (mRemoved != null) {
- for (int i=mRemoved.size()-1; i>=0; i--) {
- mFragments.addFragment(mRemoved.get(i), false);
- }
- }
- }
-
- public String getName() {
- return mName;
- }
-
- public int getTransition() {
- return mTransition;
- }
-
- public int getTransitionStyle() {
- return mTransitionStyle;
- }
- }
-
private static final class ManagedCursor {
ManagedCursor(Cursor cursor) {
mCursor = cursor;
@@ -828,7 +696,7 @@ public class Activity extends ContextThemeWrapper
protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};
private Thread mUiThread;
- private final Handler mHandler = new Handler();
+ final Handler mHandler = new Handler();
// Used for debug only
/*
@@ -952,7 +820,12 @@ public class Activity extends ContextThemeWrapper
protected void onCreate(Bundle savedInstanceState) {
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
- mFragments.dispatchCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
+ mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
+ ? mLastNonConfigurationInstances.fragments : null);
+ }
+ mFragments.dispatchCreate();
mCalled = true;
}
@@ -1237,6 +1110,10 @@ public class Activity extends ContextThemeWrapper
*/
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
+ Parcelable p = mFragments.saveAllState();
+ if (p != null) {
+ outState.putParcelable(FRAGMENTS_TAG, p);
+ }
}
/**
@@ -1541,7 +1418,8 @@ public class Activity extends ContextThemeWrapper
* {@link #onRetainNonConfigurationInstance()}.
*/
public Object getLastNonConfigurationInstance() {
- return mLastNonConfigurationInstance;
+ return mLastNonConfigurationInstances != null
+ ? mLastNonConfigurationInstances.activity : null;
}
/**
@@ -1597,8 +1475,9 @@ public class Activity extends ContextThemeWrapper
* @return Returns the object previously returned by
* {@link #onRetainNonConfigurationChildInstances()}
*/
- HashMap<String,Object> getLastNonConfigurationChildInstances() {
- return mLastNonConfigurationChildInstances;
+ HashMap<String, Object> getLastNonConfigurationChildInstances() {
+ return mLastNonConfigurationInstances != null
+ ? mLastNonConfigurationInstances.children : null;
}
/**
@@ -1612,6 +1491,21 @@ public class Activity extends ContextThemeWrapper
return null;
}
+ NonConfigurationInstances retainNonConfigurationInstances() {
+ Object activity = onRetainNonConfigurationInstance();
+ HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
+ ArrayList<Fragment> fragments = mFragments.retainNonConfig();
+ if (activity == null && children == null && fragments == null) {
+ return null;
+ }
+
+ NonConfigurationInstances nci = new NonConfigurationInstances();
+ nci.activity = activity;
+ nci.children = children;
+ nci.fragments = fragments;
+ return nci;
+ }
+
public void onLowMemory() {
mCalled = true;
}
@@ -1621,7 +1515,7 @@ public class Activity extends ContextThemeWrapper
* this activity.
*/
public FragmentTransaction openFragmentTransaction() {
- return new FragmentTransactionImpl();
+ return new BackStackEntry(mFragments);
}
/**
@@ -1811,16 +1705,6 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Finds a fragment that was identified by the given id either when inflated
- * from XML or as the container ID when added in a transaction. This only
- * returns fragments that are currently added to the activity's content.
- * @return The fragment if found or null otherwise.
- */
- public Fragment findFragmentById(int id) {
- return mFragments.findFragmentById(id);
- }
-
- /**
* Creates a new ActionBar, locates the inflated ActionBarView,
* initializes the ActionBar with the view, and sets mActionBar.
*/
@@ -1842,6 +1726,26 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Finds a fragment that was identified by the given id either when inflated
+ * from XML or as the container ID when added in a transaction. This only
+ * returns fragments that are currently added to the activity's content.
+ * @return The fragment if found or null otherwise.
+ */
+ public Fragment findFragmentById(int id) {
+ return mFragments.findFragmentById(id);
+ }
+
+ /**
+ * Finds a fragment that was identified by the given tag either when inflated
+ * from XML or as supplied when added in a transaction. This only
+ * returns fragments that are currently added to the activity's content.
+ * @return The fragment if found or null otherwise.
+ */
+ public Fragment findFragmentByTag(String tag) {
+ return mFragments.findFragmentByTag(tag);
+ }
+
+ /**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
@@ -2198,6 +2102,11 @@ public class Activity extends ContextThemeWrapper
}
public void onContentChanged() {
+ // First time content is available, let the fragment manager
+ // attach all of the fragments to it.
+ if (mFragments.mCurState < Fragment.CONTENT) {
+ mFragments.moveToState(Fragment.CONTENT, false);
+ }
}
/**
@@ -3960,44 +3869,42 @@ public class Activity extends ContextThemeWrapper
TypedArray a =
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
String fname = a.getString(com.android.internal.R.styleable.Fragment_name);
- int id = a.getInt(com.android.internal.R.styleable.Fragment_id, 0);
+ int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, 0);
String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
a.recycle();
- Constructor constructor = sConstructorMap.get(fname);
- Class clazz = null;
-
+ if (id == 0) {
+ throw new IllegalArgumentException(attrs.getPositionDescription()
+ + ": Must specify unique android:id for " + fname);
+ }
+
try {
- if (constructor == null) {
- // Class not found in the cache, see if it's real, and try to add it
- clazz = getClassLoader().loadClass(fname);
- constructor = clazz.getConstructor(sConstructorSignature);
- sConstructorMap.put(fname, constructor);
+ // If we restored from a previous state, we may already have
+ // instantiated this fragment from the state and should use
+ // that instance instead of making a new one.
+ Fragment fragment = mFragments.findFragmentById(id);
+ if (fragment == null) {
+ fragment = Fragment.instantiate(this, fname);
+ fragment.mFromLayout = true;
+ fragment.mFragmentId = id;
+ fragment.mTag = tag;
+ fragment.onInflate(this, attrs);
+ mFragments.addFragment(fragment, true);
}
- Fragment fragment = (Fragment)constructor.newInstance(sConstructorArgs);
- fragment.onInflate(this, attrs);
- mFragments.addFragment(fragment, true);
if (fragment.mView == null) {
throw new IllegalStateException("Fragment " + fname
+ " did not create a view.");
}
+ fragment.mView.setId(id);
+ if (fragment.mView.getTag() == null) {
+ fragment.mView.setTag(tag);
+ }
return fragment.mView;
-
- } catch (NoSuchMethodException e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class " + fname);
- ie.initCause(e);
- throw ie;
-
- } catch (ClassNotFoundException e) {
- // If loadClass fails, we should propagate the exception.
- throw new RuntimeException(e);
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class "
- + (clazz == null ? "<unknown>" : clazz.getName()));
+ + ": Error inflating fragment " + fname);
ie.initCause(e);
- throw new RuntimeException(ie);
+ throw ie;
}
}
@@ -4009,18 +3916,17 @@ public class Activity extends ContextThemeWrapper
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
Application application, Intent intent, ActivityInfo info, CharSequence title,
- Activity parent, String id, Object lastNonConfigurationInstance,
+ Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id,
- lastNonConfigurationInstance, null, config);
+ lastNonConfigurationInstances, config);
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
- Object lastNonConfigurationInstance,
- HashMap<String,Object> lastNonConfigurationChildInstances,
+ NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
attachBaseContext(context);
@@ -4045,8 +3951,7 @@ public class Activity extends ContextThemeWrapper
mTitle = title;
mParent = parent;
mEmbeddedID = id;
- mLastNonConfigurationInstance = lastNonConfigurationInstance;
- mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;
+ mLastNonConfigurationInstances = lastNonConfigurationInstances;
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
if (mParent != null) {
@@ -4104,7 +4009,7 @@ public class Activity extends ContextThemeWrapper
final void performResume() {
performRestart();
- mLastNonConfigurationInstance = null;
+ mLastNonConfigurationInstances = null;
// First call onResume() -before- setting mResumed, so we don't
// send out any status bar / menu notifications the client makes.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3ec8bba79716..1f2fa26d4220 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1300,8 +1300,7 @@ public final class ActivityThread {
Window window;
Activity parent;
String embeddedID;
- Object lastNonConfigurationInstance;
- HashMap<String,Object> lastNonConfigurationChildInstances;
+ Activity.NonConfigurationInstances lastNonConfigurationInstances;
boolean paused;
boolean stopped;
boolean hideForNow;
@@ -2479,7 +2478,7 @@ public final class ActivityThread {
public final Activity startActivityNow(Activity parent, String id,
Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
- Object lastNonConfigurationInstance) {
+ Activity.NonConfigurationInstances lastNonConfigurationInstances) {
ActivityRecord r = new ActivityRecord();
r.token = token;
r.ident = 0;
@@ -2488,7 +2487,7 @@ public final class ActivityThread {
r.parent = parent;
r.embeddedID = id;
r.activityInfo = activityInfo;
- r.lastNonConfigurationInstance = lastNonConfigurationInstance;
+ r.lastNonConfigurationInstances = lastNonConfigurationInstances;
if (localLOGV) {
ComponentName compname = intent.getComponent();
String name;
@@ -2610,14 +2609,12 @@ public final class ActivityThread {
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
- r.embeddedID, r.lastNonConfigurationInstance,
- r.lastNonConfigurationChildInstances, config);
+ r.embeddedID, r.lastNonConfigurationInstances, config);
if (customIntent != null) {
activity.mIntent = customIntent;
}
- r.lastNonConfigurationInstance = null;
- r.lastNonConfigurationChildInstances = null;
+ r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
@@ -3618,8 +3615,8 @@ public final class ActivityThread {
}
if (getNonConfigInstance) {
try {
- r.lastNonConfigurationInstance
- = r.activity.onRetainNonConfigurationInstance();
+ r.lastNonConfigurationInstances
+ = r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
@@ -3628,18 +3625,6 @@ public final class ActivityThread {
+ ": " + e.toString(), e);
}
}
- try {
- r.lastNonConfigurationChildInstances
- = r.activity.onRetainNonConfigurationChildInstances();
- } catch (Exception e) {
- if (!mInstrumentation.onException(r.activity, e)) {
- throw new RuntimeException(
- "Unable to retain child activities "
- + safeToComponentShortString(r.intent)
- + ": " + e.toString(), e);
- }
- }
-
}
try {
r.activity.mCalled = false;
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index c5e9e6f01c6e..1b877b226fcb 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -17,46 +17,281 @@
package android.app;
import android.content.ComponentCallbacks;
-import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+
+final class FragmentState implements Parcelable {
+ static final String VIEW_STATE_TAG = "android:view_state";
+
+ final String mClassName;
+ final boolean mFromLayout;
+ final int mSavedStateId;
+ final int mFragmentId;
+ final int mContainerId;
+ final String mTag;
+ final boolean mRetainInstance;
+
+ Bundle mSavedFragmentState;
+
+ Fragment mInstance;
+
+ public FragmentState(Fragment frag) {
+ mClassName = frag.getClass().getName();
+ mFromLayout = frag.mFromLayout;
+ mSavedStateId = frag.mSavedStateId;
+ mFragmentId = frag.mFragmentId;
+ mContainerId = frag.mContainerId;
+ mTag = frag.mTag;
+ mRetainInstance = frag.mRetainInstance;
+ }
+
+ public FragmentState(Parcel in) {
+ mClassName = in.readString();
+ mFromLayout = in.readInt() != 0;
+ mSavedStateId = in.readInt();
+ mFragmentId = in.readInt();
+ mContainerId = in.readInt();
+ mTag = in.readString();
+ mRetainInstance = in.readInt() != 0;
+ mSavedFragmentState = in.readBundle();
+ }
+
+ public Fragment instantiate(Activity activity) {
+ if (mFromLayout) {
+ return null;
+ }
+
+ if (mInstance != null) {
+ return mInstance;
+ }
+
+ try {
+ mInstance = Fragment.instantiate(activity, mClassName);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to restore fragment " + mClassName, e);
+ }
+
+ if (mSavedFragmentState != null) {
+ mSavedFragmentState.setClassLoader(activity.getClassLoader());
+ mInstance.mSavedFragmentState = mSavedFragmentState;
+ mInstance.mSavedViewState
+ = mSavedFragmentState.getSparseParcelableArray(VIEW_STATE_TAG);
+ }
+ mInstance.mSavedStateId = mSavedStateId;
+ mInstance.mFragmentId = mFragmentId;
+ mInstance.mContainerId = mContainerId;
+ mInstance.mTag = mTag;
+ mInstance.mRetainInstance = mRetainInstance;
+
+ return mInstance;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mClassName);
+ dest.writeInt(mFromLayout ? 1 : 0);
+ dest.writeInt(mSavedStateId);
+ dest.writeInt(mFragmentId);
+ dest.writeInt(mContainerId);
+ dest.writeString(mTag);
+ dest.writeInt(mRetainInstance ? 1 : 0);
+ dest.writeBundle(mSavedFragmentState);
+ }
+
+ public static final Parcelable.Creator<FragmentState> CREATOR
+ = new Parcelable.Creator<FragmentState>() {
+ public FragmentState createFromParcel(Parcel in) {
+ return new FragmentState(in);
+ }
+
+ public FragmentState[] newArray(int size) {
+ return new FragmentState[size];
+ }
+ };
+}
+
/**
* 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 {
+ private static final Object[] sConstructorArgs = new Object[0];
+
+ private static final Class[] sConstructorSignature = new Class[] { };
+
+ private static final HashMap<String, Constructor> sConstructorMap =
+ new HashMap<String, Constructor>();
+
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.
+ static final int CONTENT = 2; // View hierarchy content available.
+ static final int STARTED = 3; // Created and started, not resumed.
+ static final int RESUMED = 4; // Created started and resumed.
int mState = INITIALIZING;
+
+ // When instantiated from saved state, this is the saved state.
+ Bundle mSavedFragmentState;
+ SparseArray<Parcelable> mSavedViewState;
+
+ // Set to true if this fragment was instantiated from a layout file.
+ boolean mFromLayout;
+
+ // Number of active back stack entries this fragment is in.
int mBackStackNesting;
+
+ // Activity this fragment is attached to.
Activity mActivity;
- boolean mCalled;
+ // The optional identifier for this fragment -- either the container ID if it
+ // was dynamically added to the view hierarchy, or the ID supplied in
+ // layout.
+ int mFragmentId;
+
+ // When a fragment is being dynamically added to the view hierarchy, this
+ // is the identifier of the parent container it is being added to.
int mContainerId;
+ // The optional named tag for this fragment -- usually used to find
+ // fragments that are not part of the layout.
+ String mTag;
+
+ // If set this fragment would like its instance retained across
+ // configuration changes.
+ boolean mRetainInstance;
+
+ // If set this fragment is being retained across the current config change.
+ boolean mRetaining;
+
+ // Used to verify that subclasses call through to super class.
+ boolean mCalled;
+
+ // The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;
+
+ // The View generated for this fragment.
View mView;
+ // Used for performing save state of fragments.
+ int mSavedStateSeq = 0;
+ int mSavedStateId;
+
public Fragment() {
}
+
+ static Fragment instantiate(Activity activity, String fname)
+ throws NoSuchMethodException, ClassNotFoundException,
+ IllegalArgumentException, InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ Constructor constructor = sConstructorMap.get(fname);
+ Class clazz = null;
+
+ if (constructor == null) {
+ // Class not found in the cache, see if it's real, and try to add it
+ clazz = activity.getClassLoader().loadClass(fname);
+ constructor = clazz.getConstructor(sConstructorSignature);
+ sConstructorMap.put(fname, constructor);
+ }
+ return (Fragment)constructor.newInstance(sConstructorArgs);
+ }
+
+ void restoreViewState() {
+ if (mSavedViewState != null) {
+ mView.restoreHierarchyState(mSavedViewState);
+ mSavedViewState = null;
+ }
+ }
+
+ /**
+ * Subclasses can not override equals().
+ */
+ @Override final public boolean equals(Object o) {
+ return super.equals(o);
+ }
+
+ /**
+ * Subclasses can not override hashCode().
+ */
+ @Override final public int hashCode() {
+ return super.hashCode();
+ }
+ /**
+ * Return the identifier this fragment is known by. This is either
+ * the android:id value supplied in a layout or the container view ID
+ * supplied when adding the fragment.
+ */
+ public int getId() {
+ return mFragmentId;
+ }
+
+ /**
+ * Get the tag name of the fragment, if specified.
+ */
+ public String getTag() {
+ return mTag;
+ }
+
+ /**
+ * Return the Activity this fragment is currently associated with.
+ */
public Activity getActivity() {
return mActivity;
}
+ /**
+ * Control whether a fragment instance is retained across Activity
+ * re-creation (such as from a configuration change). This can only
+ * be used with fragments not in the back stack. If set, the fragment
+ * lifecycle will be slightly different when an activity is recreated:
+ * <ul>
+ * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
+ * will be, because the fragment is being detached from its current activity).
+ * <li> {@link #onCreate(Bundle)} will not be called since the fragment
+ * is not being re-created.
+ * <li> {@link #onAttach(Activity)} and {@link #onReady(Bundle)} <b>will</b>
+ * still be called.
+ * </ul>
+ */
+ public void setRetainInstance(boolean retain) {
+ mRetainInstance = retain;
+ }
+
+ public boolean getRetainInstance() {
+ return mRetainInstance;
+ }
+
+ /**
+ * Called when a fragment is being created as part of a view layout
+ * inflation, typically from setting the content view of an activity.
+ *
+ * @param activity The Activity that is inflating the fragment.
+ * @param attrs The attributes at the tag where the fragment is
+ * being created.
+ */
public void onInflate(Activity activity, AttributeSet attrs) {
mCalled = true;
}
+ /**
+ * Called when a fragment is first attached to its activity.
+ * {@link #onCreate(Bundle)} will be called after this.
+ */
public void onAttach(Activity activity) {
mCalled = true;
}
@@ -65,21 +300,70 @@ public class Fragment implements ComponentCallbacks {
return null;
}
+ /**
+ * Called to do initial creation of a fragment. This is called after
+ * {@link #onAttach(Activity)} and before {@link #onReady(Bundle)}.
+ * @param savedInstanceState If the fragment is being re-created from
+ * a previous saved state, this is the state.
+ */
public void onCreate(Bundle savedInstanceState) {
mCalled = true;
}
- public View onCreateView(LayoutInflater inflater, ViewGroup container) {
+ /**
+ * 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
+ * {@link #onCreate(Bundle)} and {@link #onReady(Bundle)}.
+ *
+ * @param inflater The LayoutInflater object that can be used to inflate
+ * any views in the fragment,
+ * @param container If non-null, this is the parent view that the fragment's
+ * UI should be attached to. The fragment should not add the view itself,
+ * but this can be used to generate the LayoutParams of the view.
+ * @param savedInstanceState If non-null, this fragment is being re-constructed
+ * from a previous saved state as given here.
+ *
+ * @return Return the View for the fragment's UI, or null.
+ */
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
return null;
}
- public void onRestoreInstanceState(Bundle savedInstanceState) {
+ public View getView() {
+ return mView;
+ }
+
+ /**
+ * Called when the activity is ready for the fragment to run. This is
+ * most useful for fragments that use {@link #setRetainInstance(boolean)}
+ * instance, as this tells the fragment when it is fully associated with
+ * the new activity instance. This is called after {@link #onCreate(Bundle)}
+ * and before {@link #onStart()}.
+ *
+ * @param savedInstanceState If the fragment is being re-created from
+ * a previous saved state, this is the state.
+ */
+ public void onReady(Bundle savedInstanceState) {
+ mCalled = true;
}
+ /**
+ * Called when the Fragment is visible to the user. This is generally
+ * tied to {@link Activity#onStart() Activity.onStart} of the containing
+ * Activity's lifecycle.
+ */
public void onStart() {
mCalled = true;
}
+ /**
+ * Called when the fragment is visible to the user and actively running.
+ * This is generally
+ * tied to {@link Activity#onResume() Activity.onResume} of the containing
+ * Activity's lifecycle.
+ */
public void onResume() {
mCalled = true;
}
@@ -91,14 +375,20 @@ public class Fragment implements ComponentCallbacks {
mCalled = true;
}
- public Object onRetainNonConfigurationInstance() {
- return null;
- }
-
+ /**
+ * Called when the Fragment is no longer resumed. This is generally
+ * tied to {@link Activity#onPause() Activity.onPause} of the containing
+ * Activity's lifecycle.
+ */
public void onPause() {
mCalled = true;
}
+ /**
+ * Called when the Fragment is no longer started. This is generally
+ * tied to {@link Activity#onStop() Activity.onStop} of the containing
+ * Activity's lifecycle.
+ */
public void onStop() {
mCalled = true;
}
@@ -107,10 +397,18 @@ public class Fragment implements ComponentCallbacks {
mCalled = true;
}
+ /**
+ * Called when the fragment is no longer in use. This is called
+ * after {@link #onStop()} and before {@link #onDetach()}.
+ */
public void onDestroy() {
mCalled = true;
}
+ /**
+ * Called when the fragment is no longer attached to its activity. This
+ * is called after {@link #onDestroy()}.
+ */
public void onDetach() {
mCalled = true;
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 9a4580e474fd..0bc1b8320a71 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -19,17 +19,269 @@ package android.app;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import java.util.ArrayList;
+import java.util.HashMap;
-interface BackStackState {
- public void popFromBackStack();
- public String getName();
- public int getTransition();
- public int getTransitionStyle();
+final class BackStackEntry implements FragmentTransaction, Runnable {
+ final FragmentManager mManager;
+
+ ArrayList<Fragment> mAdded;
+ ArrayList<Fragment> mRemoved;
+ int mTransition;
+ int mTransitionStyle;
+ boolean mAddToBackStack;
+ String mName;
+ boolean mCommitted;
+
+ public BackStackEntry(FragmentManager manager) {
+ mManager = manager;
+ }
+
+ public FragmentTransaction add(Fragment fragment, String tag) {
+ fragment.mTag = tag;
+ return add(fragment, 0);
+ }
+
+ public FragmentTransaction add(Fragment fragment, int containerViewId) {
+ if (fragment.mActivity != null) {
+ throw new IllegalStateException("Fragment already added: " + fragment);
+ }
+ if (mRemoved != null) {
+ mRemoved.remove(fragment);
+ }
+ if (mAdded == null) {
+ mAdded = new ArrayList<Fragment>();
+ }
+ fragment.mContainerId = fragment.mFragmentId = containerViewId;
+ mAdded.add(fragment);
+ return this;
+ }
+
+ public FragmentTransaction replace(Fragment fragment, int containerViewId) {
+ if (containerViewId == 0) {
+ throw new IllegalArgumentException("Must use non-zero containerViewId");
+ }
+ if (mManager.mFragments != null) {
+ for (int i=0; i<mManager.mFragments.size(); i++) {
+ Fragment old = mManager.mFragments.get(i);
+ if (old.mContainerId == containerViewId) {
+ remove(old);
+ }
+ }
+ }
+ return add(fragment, containerViewId);
+ }
+
+ 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 FragmentTransaction setTransition(int transition) {
+ mTransition = transition;
+ return this;
+ }
+
+ public FragmentTransaction setTransitionStyle(int styleRes) {
+ mTransitionStyle = styleRes;
+ return this;
+ }
+
+ public FragmentTransaction addToBackStack(String name) {
+ mAddToBackStack = true;
+ mName = name;
+ return this;
+ }
+
+ public void commit() {
+ if (mCommitted) throw new IllegalStateException("commit already called");
+ mCommitted = true;
+ mManager.mActivity.mHandler.post(this);
+ }
+
+ public void run() {
+ if (mRemoved != null) {
+ for (int i=mRemoved.size()-1; i>=0; i--) {
+ mManager.removeFragment(mRemoved.get(i), mTransition,
+ mTransitionStyle);
+ }
+ }
+ if (mAdded != null) {
+ for (int i=mAdded.size()-1; i>=0; i--) {
+ Fragment f = mAdded.get(i);
+ mManager.addFragment(f, false);
+ if (mAddToBackStack) {
+ f.mBackStackNesting++;
+ }
+ }
+ }
+ mManager.moveToState(mManager.mCurState, mTransition,
+ mTransitionStyle, true);
+ if (mAddToBackStack) {
+ mManager.addBackStackState(this);
+ }
+ }
+
+ public void popFromBackStack() {
+ if (mAdded != null) {
+ for (int i=mAdded.size()-1; i>=0; i--) {
+ Fragment f = mAdded.get(i);
+ if (mAddToBackStack) {
+ f.mBackStackNesting--;
+ }
+ mManager.removeFragment(f,
+ FragmentManager.reverseTransit(mTransition),
+ mTransitionStyle);
+ }
+ }
+ if (mRemoved != null) {
+ for (int i=mRemoved.size()-1; i>=0; i--) {
+ mManager.addFragment(mRemoved.get(i), false);
+ }
+ }
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getTransition() {
+ return mTransition;
+ }
+
+ public int getTransitionStyle() {
+ return mTransitionStyle;
+ }
+}
+
+final class BackStackState implements Parcelable {
+ final int[] mAdded;
+ final int[] mRemoved;
+ final int mTransition;
+ final int mTransitionStyle;
+ final String mName;
+
+ public BackStackState(FragmentManager fm, BackStackEntry bse) {
+ mAdded = buildFragmentStateList(fm, bse.mAdded);
+ mRemoved = buildFragmentStateList(fm, bse.mRemoved);
+ mTransition = bse.mTransition;
+ mTransitionStyle = bse.mTransitionStyle;
+ mName = bse.mName;
+ }
+
+ public BackStackState(Parcel in) {
+ mAdded = in.createIntArray();
+ mRemoved = in.createIntArray();
+ mTransition = in.readInt();
+ mTransitionStyle = in.readInt();
+ mName = in.readString();
+ }
+
+ public BackStackEntry instantiate(FragmentManager fm) {
+ BackStackEntry bse = new BackStackEntry(fm);
+ bse.mAdded = buildFragmentList(fm, mAdded);
+ bse.mRemoved = buildFragmentList(fm, mRemoved);
+ bse.mTransition = mTransition;
+ bse.mTransitionStyle = mTransitionStyle;
+ bse.mName = mName;
+ return bse;
+ }
+
+ static int[] buildFragmentStateList(FragmentManager fm, ArrayList<Fragment> frags) {
+ if (frags == null) return null;
+ final int N = frags.size();
+ int[] ids = new int[N];
+ for (int i=0; i<N; i++) {
+ FragmentState fs = fm.saveFragment(frags.get(i));
+ ids[i] = fs.mSavedStateId;
+ }
+ return ids;
+ }
+
+ static ArrayList<Fragment> buildFragmentList(FragmentManager fm, int[] states) {
+ if (states == null) return null;
+ final int N = states.length;
+ ArrayList<Fragment> frags = new ArrayList<Fragment>(N);
+ for (int i=0; i<N; i++) {
+ frags.add(fm.mRestoredFragments.get(states[i]).instantiate(fm.mActivity));
+ }
+ return frags;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeIntArray(mAdded);
+ dest.writeIntArray(mRemoved);
+ dest.writeInt(mTransition);
+ dest.writeInt(mTransitionStyle);
+ dest.writeString(mName);
+ }
+
+ public static final Parcelable.Creator<BackStackState> CREATOR
+ = new Parcelable.Creator<BackStackState>() {
+ public BackStackState createFromParcel(Parcel in) {
+ return new BackStackState(in);
+ }
+
+ public BackStackState[] newArray(int size) {
+ return new BackStackState[size];
+ }
+ };
+}
+
+final class FragmentManagerState implements Parcelable {
+ FragmentState[] mFragments;
+ int[] mAdded;
+ BackStackState[] mBackStack;
+
+ public FragmentManagerState() {
+ }
+
+ public FragmentManagerState(Parcel in) {
+ mFragments = in.createTypedArray(FragmentState.CREATOR);
+ mAdded = in.createIntArray();
+ mBackStack = in.createTypedArray(BackStackState.CREATOR);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedArray(mFragments, flags);
+ dest.writeIntArray(mAdded);
+ dest.writeTypedArray(mBackStack, flags);
+ }
+
+ public static final Parcelable.Creator<FragmentManagerState> CREATOR
+ = new Parcelable.Creator<FragmentManagerState>() {
+ public FragmentManagerState createFromParcel(Parcel in) {
+ return new FragmentManagerState(in);
+ }
+
+ public FragmentManagerState[] newArray(int size) {
+ return new FragmentManagerState[size];
+ }
+ };
}
/**
@@ -38,11 +290,20 @@ interface BackStackState {
*/
public class FragmentManager {
ArrayList<Fragment> mFragments;
- ArrayList<BackStackState> mBackStack;
+ ArrayList<BackStackEntry> mBackStack;
int mCurState = Fragment.INITIALIZING;
Activity mActivity;
+ int mSaveStateSeq = 0;
+
+ // Temporary vars for state save and restore.
+ int mCurSaveId = 0;
+ HashMap<Fragment, FragmentState> mSavedFragments;
+ SparseArray<FragmentState> mRestoredFragments;
+ Bundle mStateBundle = null;
+ SparseArray<Parcelable> mStateArray = null;
+
Animation loadAnimation(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
Animation animObj = fragment.onCreateAnimation(transitionStyle, enter);
@@ -89,32 +350,60 @@ public class FragmentManager {
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);
+ if (!f.mRetaining) {
+ f.mCalled = false;
+ f.onCreate(f.mSavedFragmentState);
+ if (!f.mCalled) {
+ throw new SuperNotCalledException("Fragment " + f
+ + " did not call through to super.onCreate()");
}
}
- f.mContainer = container;
- f.mView = f.onCreateView(mActivity.getLayoutInflater(), container);
- if (container != null && f.mView != null) {
- Animation anim = loadAnimation(f, transit, true, transitionStyle);
- if (anim != null) {
- f.mView.setAnimation(anim);
+ f.mRetaining = false;
+ if (f.mFromLayout) {
+ // For fragments that are part of the content view
+ // layout, we need to instantiate the view immediately
+ // and the inflater will take care of adding it.
+ f.mView = f.onCreateView(mActivity.getLayoutInflater(),
+ null, f.mSavedFragmentState);
+ }
+ case Fragment.CONTENT:
+ if (newState > Fragment.CONTENT) {
+ if (!f.mFromLayout) {
+ 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, f.mSavedFragmentState);
+ if (f.mView != null) {
+ f.mView.setSaveFromParentEnabled(false);
+ if (container != null) {
+ Animation anim = loadAnimation(f, transit, true,
+ transitionStyle);
+ if (anim != null) {
+ f.mView.setAnimation(anim);
+ }
+ container.addView(f.mView);
+ f.restoreViewState();
+ }
+ }
+ }
+
+ f.mCalled = false;
+ f.onReady(f.mSavedFragmentState);
+ if (!f.mCalled) {
+ throw new SuperNotCalledException("Fragment " + f
+ + " did not call through to super.onReady()");
}
- container.addView(f.mView);
+ f.mSavedFragmentState = null;
}
-
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
f.mCalled = false;
@@ -154,24 +443,39 @@ public class FragmentManager {
+ " did not call through to super.onStop()");
}
}
- case Fragment.CREATED:
- if (newState < Fragment.CREATED) {
- if (f.mContainer != null) {
- Animation anim = loadAnimation(f, transit, false, transitionStyle);
- if (anim != null) {
- f.mView.setAnimation(anim);
+ case Fragment.CONTENT:
+ if (newState < Fragment.CONTENT) {
+ if (f.mView != null) {
+ // Need to save the current view state if not
+ // done already.
+ if (!mActivity.isFinishing()) {
+ saveFragmentViewState(f);
+ }
+ if (f.mContainer != null) {
+ if (mCurState > Fragment.INITIALIZING) {
+ Animation anim = loadAnimation(f, transit, false,
+ transitionStyle);
+ if (anim != null) {
+ f.mView.setAnimation(anim);
+ }
+ }
+ f.mContainer.removeView(f.mView);
}
- 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()");
+ }
+ case Fragment.CREATED:
+ if (newState < Fragment.CREATED) {
+ if (!f.mRetaining) {
+ 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) {
@@ -227,7 +531,19 @@ public class FragmentManager {
if (mFragments != null) {
for (int i=mFragments.size()-1; i>=0; i--) {
Fragment f = mFragments.get(i);
- if (f.mContainerId == id) {
+ if (f.mFragmentId == id) {
+ return f;
+ }
+ }
+ }
+ return null;
+ }
+
+ public Fragment findFragmentByTag(String tag) {
+ if (mFragments != null && tag != null) {
+ for (int i=mFragments.size()-1; i>=0; i--) {
+ Fragment f = mFragments.get(i);
+ if (tag.equals(f.mTag)) {
return f;
}
}
@@ -235,9 +551,9 @@ public class FragmentManager {
return null;
}
- public void addBackStackState(BackStackState state) {
+ public void addBackStackState(BackStackEntry state) {
if (mBackStack == null) {
- mBackStack = new ArrayList<BackStackState>();
+ mBackStack = new ArrayList<BackStackEntry>();
}
mBackStack.add(state);
}
@@ -251,7 +567,7 @@ public class FragmentManager {
if (last < 0) {
return false;
}
- final BackStackState bss = mBackStack.remove(last);
+ final BackStackEntry bss = mBackStack.remove(last);
handler.post(new Runnable() {
public void run() {
bss.popFromBackStack();
@@ -262,7 +578,7 @@ public class FragmentManager {
} else {
int index = mBackStack.size()-1;
while (index >= 0) {
- BackStackState bss = mBackStack.get(index);
+ BackStackEntry bss = mBackStack.get(index);
if (name.equals(bss.getName())) {
break;
}
@@ -270,7 +586,8 @@ public class FragmentManager {
if (index < 0 || index == mBackStack.size()-1) {
return false;
}
- final ArrayList<BackStackState> states = new ArrayList<BackStackState>();
+ final ArrayList<BackStackEntry> states
+ = new ArrayList<BackStackEntry>();
for (int i=mBackStack.size()-1; i>index; i--) {
states.add(mBackStack.remove(i));
}
@@ -286,12 +603,189 @@ public class FragmentManager {
return true;
}
+ ArrayList<Fragment> retainNonConfig() {
+ ArrayList<Fragment> fragments = null;
+ if (mFragments != null) {
+ for (int i=0; i<mFragments.size(); i++) {
+ Fragment f = mFragments.get(i);
+ if (f.mBackStackNesting <= 0 && f.mRetainInstance && f.mTag != null) {
+ if (fragments == null) {
+ fragments = new ArrayList<Fragment>();
+ }
+ fragments.add(f);
+ f.mRetaining = true;
+ }
+ }
+ }
+ return fragments;
+ }
+
+ void saveFragmentViewState(Fragment f) {
+ if (f.mSavedViewState != null) {
+ return;
+ }
+ if (mStateArray == null) {
+ mStateArray = new SparseArray<Parcelable>();
+ }
+ f.mView.saveHierarchyState(mStateArray);
+ if (mStateArray.size() > 0) {
+ f.mSavedViewState = mStateArray;
+ mStateArray = null;
+ }
+ }
+
+ FragmentState saveFragment(Fragment f) {
+ if (mSavedFragments != null) {
+ FragmentState fs = mSavedFragments.get(f);
+ if (fs != null) {
+ return fs;
+ }
+ } else {
+ mSavedFragments = new HashMap<Fragment, FragmentState>();
+ }
+
+ f.mSavedStateSeq = mSaveStateSeq;
+ f.mSavedStateId = ++mCurSaveId;
+
+ FragmentState fs = new FragmentState(f);
+ mSavedFragments.put(f, fs);
+
+ if (mStateBundle == null) {
+ mStateBundle = new Bundle();
+ }
+ f.onSaveInstanceState(mStateBundle);
+ if (!mStateBundle.isEmpty()) {
+ fs.mSavedFragmentState = mStateBundle;
+ mStateBundle = null;
+ }
+
+ if (f.mView != null) {
+ saveFragmentViewState(f);
+ if (f.mSavedViewState != null) {
+ if (fs.mSavedFragmentState == null) {
+ fs.mSavedFragmentState = new Bundle();
+ }
+ fs.mSavedFragmentState.putSparseParcelableArray(
+ FragmentState.VIEW_STATE_TAG, f.mSavedViewState);
+ }
+ }
+
+ return fs;
+ }
+
+ Parcelable saveAllState() {
+ mSavedFragments = null;
+ mSaveStateSeq++;
+
+ if (mFragments == null) {
+ return null;
+ }
+
+ int[] added = null;
+ BackStackState[] backStack = null;
+
+ // First collect all active fragments.
+ int N = mFragments.size();
+ if (N > 0) {
+ added = new int[N];
+ for (int i=0; i<N; i++) {
+ added[i] = saveFragment(mFragments.get(i)).mSavedStateId;
+ }
+ }
+
+ // Now save back stack.
+ if (mBackStack != null) {
+ N = mBackStack.size();
+ if (N > 0) {
+ backStack = new BackStackState[N];
+ for (int i=0; i<N; i++) {
+ backStack[i] = new BackStackState(this, mBackStack.get(i));
+ }
+ }
+ }
+
+ if (mSavedFragments == null) {
+ return null;
+ }
+ N = mSavedFragments.size();
+ if (N <= 0) {
+ return null;
+ }
+
+ FragmentManagerState fms = new FragmentManagerState();
+ fms.mFragments = new FragmentState[N];
+ int i = 0;
+ for (FragmentState fs : mSavedFragments.values()) {
+ fms.mFragments[i] = fs;
+ i++;
+ }
+ fms.mAdded = added;
+ fms.mBackStack = backStack;
+ return fms;
+ }
+
+ void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
+ // If there is no saved state at all, then there can not be
+ // any nonConfig fragments either, so that is that.
+ if (state == null) return;
+ FragmentManagerState fms = (FragmentManagerState)state;
+ if (fms.mFragments == null) return;
+
+ // First build our lookup table of all known Fragment objects.
+ mRestoredFragments = new SparseArray<FragmentState>();
+ for (int i=0; i<fms.mFragments.length; i++) {
+ FragmentState fs = fms.mFragments[i];
+ mRestoredFragments.put(fs.mSavedStateId, fs);
+ }
+
+ // Stick any non-config instances we are retaining directly
+ // into the lookup table, so we don't try to instantiate them again.
+ if (nonConfig != null) {
+ for (int i=0; i<nonConfig.size(); i++) {
+ Fragment f = nonConfig.get(i);
+ FragmentState fs = mRestoredFragments.get(f.mSavedStateId);
+ fs.mInstance = f;
+ f.mSavedViewState = null;
+ if (fs.mSavedFragmentState != null) {
+ f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
+ FragmentState.VIEW_STATE_TAG);
+ }
+ addFragment(f, false);
+ }
+ }
+
+ // Now build our data structures from the saved state, instantiating
+ // the fragment objects as needed.
+ if (fms.mAdded != null) {
+ if (mFragments == null) {
+ mFragments = new ArrayList<Fragment>(fms.mAdded.length);
+ }
+ for (int i=0; i<fms.mAdded.length; i++) {
+ FragmentState fs = mRestoredFragments.get(fms.mAdded[i]);
+ Fragment f = fs.instantiate(mActivity);
+ // This will return null if this is a layout fragment,
+ // since the instance for such a fragment will be created
+ // later during layout inflation.
+ if (f != null) {
+ mFragments.add(f);
+ }
+ }
+ }
+ if (fms.mBackStack != null) {
+ mBackStack = new ArrayList<BackStackEntry>(fms.mBackStack.length);
+ for (int i=0; i<fms.mBackStack.length; i++) {
+ BackStackEntry bse = fms.mBackStack[i].instantiate(this);
+ mBackStack.add(bse);
+ }
+ }
+ }
+
public void attachActivity(Activity activity) {
if (mActivity != null) throw new IllegalStateException();
mActivity = activity;
}
- public void dispatchCreate(Bundle state) {
+ public void dispatchCreate() {
moveToState(Fragment.CREATED, false);
}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 94d3b88a79a2..a28530fecae8 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -4,7 +4,7 @@ package android.app;
* API for performing a set of Fragment operations.
*/
public interface FragmentTransaction {
- public FragmentTransaction add(Fragment fragment);
+ public FragmentTransaction add(Fragment fragment, String tag);
public FragmentTransaction add(Fragment fragment, int containerViewId);
public FragmentTransaction replace(Fragment fragment, int containerViewId);
public FragmentTransaction remove(Fragment fragment);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b937dd6b6b82..cbb1c1a3bb2a 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -997,8 +997,10 @@ public class Instrumentation {
IllegalAccessException {
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
- activity.attach(context, aThread, this, token, application, intent, info, title,
- parent, id, lastNonConfigurationInstance, new Configuration());
+ activity.attach(context, aThread, this, token, application, intent,
+ info, title, parent, id,
+ (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
+ new Configuration());
return activity;
}
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index a24fcae26639..be8f413d63a8 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -112,11 +112,16 @@ public class LocalActivityManager {
if (r.curState == INITIALIZING) {
// Get the lastNonConfigurationInstance for the activity
- HashMap<String,Object> lastNonConfigurationInstances =
- mParent.getLastNonConfigurationChildInstances();
- Object instance = null;
+ HashMap<String, Object> lastNonConfigurationInstances =
+ mParent.getLastNonConfigurationChildInstances();
+ Object instanceObj = null;
if (lastNonConfigurationInstances != null) {
- instance = lastNonConfigurationInstances.get(r.id);
+ instanceObj = lastNonConfigurationInstances.get(r.id);
+ }
+ Activity.NonConfigurationInstances instance = null;
+ if (instanceObj != null) {
+ instance = new Activity.NonConfigurationInstances();
+ instance.activity = instanceObj;
}
// We need to have always created the activity.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 11e5ad138807..527b4f44fea9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -887,6 +887,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000;
/**
+ * <p>Indicates that the view hierarchy should stop saving state when
+ * it reaches this view. If state saving is initiated immediately at
+ * the view, it will be allowed.
+ * {@hide}
+ */
+ static final int PARENT_SAVE_DISABLED = 0x20000000;
+
+ /**
+ * <p>Mask for use with setFlags indicating bits used for PARENT_SAVE_DISABLED.</p>
+ * {@hide}
+ */
+ static final int PARENT_SAVE_DISABLED_MASK = 0x20000000;
+
+ /**
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
* should add all focusable Views regardless if they are focusable in touch mode.
*/
@@ -3342,6 +3356,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
+ * Indicates whether the entire hierarchy under this view will save its
+ * state when a state saving traversal occurs from its parent. The default
+ * is true; if false, these views will not be saved unless
+ * {@link #saveHierarchyState(SparseArray)} is called directly on this view.
+ *
+ * @return Returns true if the view state saving from parent is enabled, else false.
+ *
+ * @see #setSaveFromParentEnabled(boolean)
+ */
+ public boolean isSaveFromParentEnabled() {
+ return (mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED;
+ }
+
+ /**
+ * Controls whether the entire hierarchy under this view will save its
+ * state when a state saving traversal occurs from its parent. The default
+ * is true; if false, these views will not be saved unless
+ * {@link #saveHierarchyState(SparseArray)} is called directly on this view.
+ *
+ * @param enabled Set to false to <em>disable</em> state saving, or true
+ * (the default) to allow it.
+ *
+ * @see #isSaveFromParentEnabled()
+ * @see #setId(int)
+ * @see #onSaveInstanceState()
+ */
+ public void setSaveFromParentEnabled(boolean enabled) {
+ setFlags(enabled ? 0 : PARENT_SAVE_DISABLED, PARENT_SAVE_DISABLED_MASK);
+ }
+
+
+ /**
* Returns whether this View is able to take focus.
*
* @return True if this view can take focus, or false otherwise.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c71f4bde6630..9329d94dad44 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1181,7 +1181,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
- children[i].dispatchSaveInstanceState(container);
+ View c = children[i];
+ if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
+ c.dispatchSaveInstanceState(container);
+ }
}
}
@@ -1206,7 +1209,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
- children[i].dispatchRestoreInstanceState(container);
+ View c = children[i];
+ if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
+ c.dispatchRestoreInstanceState(container);
+ }
}
}