Some fragment stuff:
Fix issue #2975886: Make getTargetFragment() survive rotation events with
retained fragments. We now fix up the fragment pointer when restoring state.
Fix issue #2919928: In PreferenceFragment, addPreferencesFromResources() is
not effective when called after onActivityCreated(). Note to self: do not use
a what code of 0. Maybe that should be documented (I'll do it in gingerbread).
Hopefully implement #2992753: DialogFragment.dismiss will NPE if called too soon
(before attached to activity). We now keep track of the FragmentManager
separately from the activity, and set that as soon as the fragment is part of a
transaction.
Investigate issue #2988876: NPE when device orientation is changed. The NPE is
because of the app trying to do a fragment transaction in onPause(). This is
fundamentally not viable, since (a) the activity will be gone before we ever
have a chance to process the message to commit the transaction, and (b) even if
we did try to commit the transaction earlier, this would be done after
onSaveInstanceState() and thus not work in cases where the activity gets killed
in the background. So instead, we'll just throw an immediate exception if you
try to do this.
Change-Id: Iea62b50eb79f066af2471fce86836d073398f4f7
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3dcdc0a..97aa23e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4058,6 +4058,7 @@
fragment.mFragmentId = id;
fragment.mTag = tag;
fragment.mImmediateActivity = this;
+ fragment.mFragmentManager = mFragments;
// If this fragment is newly instantiated (either right now, or
// from last saved state), then give it the attributes to
// initialize itself.
diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackEntry.java
index 71fd5e5..296b495 100644
--- a/core/java/android/app/BackStackEntry.java
+++ b/core/java/android/app/BackStackEntry.java
@@ -205,6 +205,7 @@
throw new IllegalStateException("Fragment already added: " + fragment);
}
fragment.mImmediateActivity = mManager.mActivity;
+ fragment.mFragmentManager = mManager;
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 909e81f..1cbed79 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -94,6 +94,7 @@
mInstance.mContainerId = mContainerId;
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
+ mInstance.mFragmentManager = activity.mFragments;
return mInstance;
}
@@ -318,6 +319,11 @@
// Number of active back stack entries this fragment is in.
int mBackStackNesting;
+ // The fragment manager we are associated with. Set as soon as the
+ // fragment is used in a transaction; cleared after it has been removed
+ // from all transactions.
+ FragmentManager mFragmentManager;
+
// Set as soon as a fragment is added to a transaction (or removed),
// to be able to do validation.
Activity mImmediateActivity;
@@ -578,10 +584,13 @@
/**
* Return the FragmentManager for interacting with fragments associated
- * with this fragment's activity.
+ * with this fragment's activity. Note that this will be non-null slightly
+ * before {@link #getActivity()}, in the time from when the fragment is
+ * placed in a {@link FragmentTransaction} until it is committed and
+ * attached to its activity.
*/
final public FragmentManager getFragmentManager() {
- return mActivity.mFragments;
+ return mFragmentManager;
}
/**
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 85a9d60..a704ec8 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -192,6 +192,7 @@
Activity mActivity;
boolean mNeedMenuInvalidate;
+ boolean mStateSaved;
// Temporary vars for state save and restore.
Bundle mStateBundle = null;
@@ -484,6 +485,7 @@
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onDetach()");
}
+ f.mImmediateActivity = null;
f.mActivity = null;
}
}
@@ -678,6 +680,10 @@
}
public void enqueueAction(Runnable action) {
+ if (mStateSaved) {
+ throw new IllegalStateException(
+ "Can not perform this action after onSaveInstanceState");
+ }
synchronized (this) {
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
@@ -888,6 +894,8 @@
}
Parcelable saveAllState() {
+ mStateSaved = true;
+
if (mActive == null || mActive.size() <= 0) {
return null;
}
@@ -1029,6 +1037,22 @@
}
}
+ // Update the target of all retained fragments.
+ if (nonConfig != null) {
+ for (int i=0; i<nonConfig.size(); i++) {
+ Fragment f = nonConfig.get(i);
+ if (f.mTarget != null) {
+ if (f.mTarget.mIndex < mActive.size()) {
+ f.mTarget = mActive.get(f.mTarget.mIndex);
+ } else {
+ Log.w(TAG, "Re-attaching retained fragment " + f
+ + " target no longer exists: " + f.mTarget);
+ f.mTarget = null;
+ }
+ }
+ }
+ }
+
// Build the list of currently added fragments.
if (fms.mAdded != null) {
mAdded = new ArrayList<Fragment>(fms.mAdded.length);
@@ -1070,18 +1094,22 @@
}
public void dispatchCreate() {
+ mStateSaved = false;
moveToState(Fragment.CREATED, false);
}
public void dispatchActivityCreated() {
+ mStateSaved = false;
moveToState(Fragment.ACTIVITY_CREATED, false);
}
public void dispatchStart() {
+ mStateSaved = false;
moveToState(Fragment.STARTED, false);
}
public void dispatchResume() {
+ mStateSaved = false;
moveToState(Fragment.RESUMED, false);
}
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index c28ccd3..2efb09d 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -180,8 +180,8 @@
*/
private static final int FIRST_REQUEST_CODE = 100;
- private static final int MSG_BIND_PREFERENCES = 0;
- private static final int MSG_BUILD_HEADERS = 1;
+ private static final int MSG_BIND_PREFERENCES = 1;
+ private static final int MSG_BUILD_HEADERS = 2;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 0b5dffe..479497a 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -106,7 +106,7 @@
*/
private static final int FIRST_REQUEST_CODE = 100;
- private static final int MSG_BIND_PREFERENCES = 0;
+ private static final int MSG_BIND_PREFERENCES = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {