summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adam Powell <adamp@google.com> 2016-02-04 16:20:37 -0800
committer Adam Powell <adamp@google.com> 2016-02-08 19:10:44 +0000
commit44ba79e47d6db54e5501f994880fa09eb880c185 (patch)
tree4d20af401f2ee2a8d69f145260a0fab1ea612ce8
parent8585ed66b947ef30d6a43f0bb91885ed0c5ebfb4 (diff)
Permit setRetainInstance(true) on nested fragments, framework edition
Save arbitrarily nested fragments across config changes as nonconfiguration objects. This permits the use of retain-instance child fragments as arbitrary opaque dependencies within other fragments. Change-Id: Ia6640b76cfcf7ec28ba252628957a0c14863e957 (cherry picked from commit 7466be66263d5ebffb786ea402d9ed6e36c254f0)
-rw-r--r--api/current.txt9
-rw-r--r--api/system-current.txt9
-rw-r--r--api/test-current.txt9
-rw-r--r--core/java/android/app/Activity.java4
-rw-r--r--core/java/android/app/Fragment.java64
-rw-r--r--core/java/android/app/FragmentController.java26
-rw-r--r--core/java/android/app/FragmentManager.java72
-rw-r--r--core/java/android/app/FragmentManagerNonConfig.java54
8 files changed, 187 insertions, 60 deletions
diff --git a/api/current.txt b/api/current.txt
index 7779ec17d2ef..ae343cdc5e8b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4531,10 +4531,12 @@ package android.app {
method public void noteStateNotSaved();
method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
method public void reportLoaderStart();
- method public void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+ method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+ method public void restoreAllState(android.os.Parcelable, android.app.FragmentManagerNonConfig);
method public void restoreLoaderNonConfig(android.util.ArrayMap<java.lang.String, android.app.LoaderManager>);
method public android.util.ArrayMap<java.lang.String, android.app.LoaderManager> retainLoaderNonConfig();
- method public java.util.List<android.app.Fragment> retainNonConfig();
+ method public android.app.FragmentManagerNonConfig retainNestedNonConfig();
+ method public deprecated java.util.List<android.app.Fragment> retainNonConfig();
method public android.os.Parcelable saveAllState();
}
@@ -4594,6 +4596,9 @@ package android.app {
method public abstract void onBackStackChanged();
}
+ public class FragmentManagerNonConfig {
+ }
+
public abstract class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1f9d2916f04d..bcd39fe3272e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4664,10 +4664,12 @@ package android.app {
method public void noteStateNotSaved();
method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
method public void reportLoaderStart();
- method public void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+ method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+ method public void restoreAllState(android.os.Parcelable, android.app.FragmentManagerNonConfig);
method public void restoreLoaderNonConfig(android.util.ArrayMap<java.lang.String, android.app.LoaderManager>);
method public android.util.ArrayMap<java.lang.String, android.app.LoaderManager> retainLoaderNonConfig();
- method public java.util.List<android.app.Fragment> retainNonConfig();
+ method public android.app.FragmentManagerNonConfig retainNestedNonConfig();
+ method public deprecated java.util.List<android.app.Fragment> retainNonConfig();
method public android.os.Parcelable saveAllState();
}
@@ -4727,6 +4729,9 @@ package android.app {
method public abstract void onBackStackChanged();
}
+ public class FragmentManagerNonConfig {
+ }
+
public abstract class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 6e6f99ac5f66..c1cd2750424e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4531,10 +4531,12 @@ package android.app {
method public void noteStateNotSaved();
method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
method public void reportLoaderStart();
- method public void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+ method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+ method public void restoreAllState(android.os.Parcelable, android.app.FragmentManagerNonConfig);
method public void restoreLoaderNonConfig(android.util.ArrayMap<java.lang.String, android.app.LoaderManager>);
method public android.util.ArrayMap<java.lang.String, android.app.LoaderManager> retainLoaderNonConfig();
- method public java.util.List<android.app.Fragment> retainNonConfig();
+ method public android.app.FragmentManagerNonConfig retainNestedNonConfig();
+ method public deprecated java.util.List<android.app.Fragment> retainNonConfig();
method public android.os.Parcelable saveAllState();
}
@@ -4594,6 +4596,9 @@ package android.app {
method public abstract void onBackStackChanged();
}
+ public class FragmentManagerNonConfig {
+ }
+
public abstract class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e0eb3895b3d2..622012ecf2e2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -753,7 +753,7 @@ public class Activity extends ContextThemeWrapper
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
- List<Fragment> fragments;
+ FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
@@ -2070,7 +2070,7 @@ public class Activity extends ContextThemeWrapper
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
- List<Fragment> fragments = mFragments.retainNonConfig();
+ FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 688c9f8e4fc4..02a898b33dd1 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -97,35 +97,34 @@ final class FragmentState implements Parcelable {
mSavedFragmentState = in.readBundle();
}
- public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
- if (mInstance != null) {
- return mInstance;
- }
-
- final Context context = host.getContext();
- if (mArguments != null) {
- mArguments.setClassLoader(context.getClassLoader());
- }
+ public Fragment instantiate(FragmentHostCallback host, Fragment parent,
+ FragmentManagerNonConfig childNonConfig) {
+ if (mInstance == null) {
+ final Context context = host.getContext();
+ if (mArguments != null) {
+ mArguments.setClassLoader(context.getClassLoader());
+ }
- mInstance = Fragment.instantiate(context, mClassName, mArguments);
-
- if (mSavedFragmentState != null) {
- mSavedFragmentState.setClassLoader(context.getClassLoader());
- mInstance.mSavedFragmentState = mSavedFragmentState;
- }
- mInstance.setIndex(mIndex, parent);
- mInstance.mFromLayout = mFromLayout;
- mInstance.mRestored = true;
- mInstance.mFragmentId = mFragmentId;
- mInstance.mContainerId = mContainerId;
- mInstance.mTag = mTag;
- mInstance.mRetainInstance = mRetainInstance;
- mInstance.mDetached = mDetached;
- mInstance.mHidden = mHidden;
- mInstance.mFragmentManager = host.mFragmentManager;
- if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
- "Instantiated fragment " + mInstance);
+ mInstance = Fragment.instantiate(context, mClassName, mArguments);
+ if (mSavedFragmentState != null) {
+ mSavedFragmentState.setClassLoader(context.getClassLoader());
+ mInstance.mSavedFragmentState = mSavedFragmentState;
+ }
+ mInstance.setIndex(mIndex, parent);
+ mInstance.mFromLayout = mFromLayout;
+ mInstance.mRestored = true;
+ mInstance.mFragmentId = mFragmentId;
+ mInstance.mContainerId = mContainerId;
+ mInstance.mTag = mTag;
+ mInstance.mRetainInstance = mRetainInstance;
+ mInstance.mDetached = mDetached;
+ mInstance.mHidden = mHidden;
+ mInstance.mFragmentManager = host.mFragmentManager;
+ if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
+ "Instantiated fragment " + mInstance);
+ }
+ mInstance.mChildNonConfig = childNonConfig;
return mInstance;
}
@@ -433,6 +432,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
// Private fragment manager for child fragments inside of this one.
FragmentManagerImpl mChildFragmentManager;
+ // For use when restoring fragment state and descendant fragments are retained.
+ // This state is set by FragmentState.instantiate and cleared in onCreate.
+ FragmentManagerNonConfig mChildNonConfig;
+
// If this Fragment is contained in another Fragment, this is that container.
Fragment mParentFragment;
@@ -975,10 +978,6 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* </ul>
*/
public void setRetainInstance(boolean retain) {
- if (retain && mParentFragment != null) {
- throw new IllegalStateException(
- "Can't retain fragements that are nested in other fragments");
- }
mRetainInstance = retain;
}
@@ -1436,7 +1435,8 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
if (mChildFragmentManager == null) {
instantiateChildFragmentManager();
}
- mChildFragmentManager.restoreAllState(p, null);
+ mChildFragmentManager.restoreAllState(p, mChildNonConfig);
+ mChildNonConfig = null;
mChildFragmentManager.dispatchCreate();
}
}
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index a9270bc43e09..57b0ff134818 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -117,16 +117,40 @@ public class FragmentController {
* instances retained across configuration changes.
*
* @see #retainNonConfig()
+ *
+ * @deprecated use {@link #restoreAllState(Parcelable, FragmentManagerNonConfig)}
*/
public void restoreAllState(Parcelable state, List<Fragment> nonConfigList) {
- mHost.mFragmentManager.restoreAllState(state, nonConfigList);
+ mHost.mFragmentManager.restoreAllState(state,
+ new FragmentManagerNonConfig(nonConfigList, null));
+ }
+
+ /**
+ * Restores the saved state for all Fragments. The given FragmentManagerNonConfig are Fragment
+ * instances retained across configuration changes, including nested fragments
+ *
+ * @see #retainNestedNonConfig()
+ */
+ public void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
+ mHost.mFragmentManager.restoreAllState(state, nonConfig);
}
/**
* Returns a list of Fragments that have opted to retain their instance across
* configuration changes.
+ *
+ * @deprecated use {@link #retainNestedNonConfig()} to also track retained
+ * nested child fragments
*/
public List<Fragment> retainNonConfig() {
+ return mHost.mFragmentManager.retainNonConfig().getFragments();
+ }
+
+ /**
+ * Returns a nested tree of Fragments that have opted to retain their instance across
+ * configuration changes.
+ */
+ public FragmentManagerNonConfig retainNestedNonConfig() {
return mHost.mFragmentManager.retainNonConfig();
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 8b5dbaef7452..4c8761c86b93 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1672,24 +1672,47 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
return true;
}
-
- ArrayList<Fragment> retainNonConfig() {
+
+ FragmentManagerNonConfig retainNonConfig() {
ArrayList<Fragment> fragments = null;
+ ArrayList<FragmentManagerNonConfig> childFragments = null;
if (mActive != null) {
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.get(i);
- if (f != null && f.mRetainInstance) {
- if (fragments == null) {
- fragments = new ArrayList<Fragment>();
+ if (f != null) {
+ if (f.mRetainInstance) {
+ if (fragments == null) {
+ fragments = new ArrayList<>();
+ }
+ fragments.add(f);
+ f.mRetaining = true;
+ f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
+ if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
+ }
+ boolean addedChild = false;
+ if (f.mChildFragmentManager != null) {
+ FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig();
+ if (child != null) {
+ if (childFragments == null) {
+ childFragments = new ArrayList<>();
+ for (int j = 0; j < i; j++) {
+ childFragments.add(null);
+ }
+ }
+ childFragments.add(child);
+ addedChild = true;
+ }
+ }
+ if (childFragments != null && !addedChild) {
+ childFragments.add(null);
}
- fragments.add(f);
- f.mRetaining = true;
- f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
- if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
}
}
}
- return fragments;
+ if (fragments == null && childFragments == null) {
+ return null;
+ }
+ return new FragmentManagerNonConfig(fragments, childFragments);
}
void saveFragmentViewState(Fragment f) {
@@ -1846,18 +1869,23 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
return fms;
}
- void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
+ void restoreAllState(Parcelable state, FragmentManagerNonConfig 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.mActive == null) return;
-
+
+ List<FragmentManagerNonConfig> childNonConfigs = null;
+
// First re-attach any non-config instances we are retaining back
// to their saved state, 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);
+ List<Fragment> nonConfigFragments = nonConfig.getFragments();
+ childNonConfigs = nonConfig.getChildNonConfigs();
+ final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
+ for (int i = 0; i < count; i++) {
+ Fragment f = nonConfigFragments.get(i);
if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
FragmentState fs = fms.mActive[f.mIndex];
fs.mInstance = f;
@@ -1877,14 +1905,18 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
// Build the full list of active fragments, instantiating them from
// their saved state.
- mActive = new ArrayList<Fragment>(fms.mActive.length);
+ mActive = new ArrayList<>(fms.mActive.length);
if (mAvailIndices != null) {
mAvailIndices.clear();
}
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
- Fragment f = fs.instantiate(mHost, mParent);
+ FragmentManagerNonConfig childNonConfig = null;
+ if (childNonConfigs != null && i < childNonConfigs.size()) {
+ childNonConfig = childNonConfigs.get(i);
+ }
+ Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
mActive.add(f);
// Now that the fragment is instantiated (or came from being
@@ -1894,7 +1926,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
} else {
mActive.add(null);
if (mAvailIndices == null) {
- mAvailIndices = new ArrayList<Integer>();
+ mAvailIndices = new ArrayList<>();
}
if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
mAvailIndices.add(i);
@@ -1903,8 +1935,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
// Update the target of all retained fragments.
if (nonConfig != null) {
- for (int i=0; i<nonConfig.size(); i++) {
- Fragment f = nonConfig.get(i);
+ List<Fragment> nonConfigFragments = nonConfig.getFragments();
+ final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
+ for (int i = 0; i < count; i++) {
+ Fragment f = nonConfigFragments.get(i);
if (f.mTargetIndex >= 0) {
if (f.mTargetIndex < mActive.size()) {
f.mTarget = mActive.get(f.mTargetIndex);
diff --git a/core/java/android/app/FragmentManagerNonConfig.java b/core/java/android/app/FragmentManagerNonConfig.java
new file mode 100644
index 000000000000..50d3797dd59d
--- /dev/null
+++ b/core/java/android/app/FragmentManagerNonConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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 java.util.List;
+
+/**
+ * FragmentManagerNonConfig stores the retained instance fragments across
+ * activity recreation events.
+ *
+ * <p>Apps should treat objects of this type as opaque, returned by
+ * and passed to the state save and restore process for fragments in
+ * {@link FragmentController#retainNonConfig()} and
+ * {@link FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)}.</p>
+ */
+public class FragmentManagerNonConfig {
+ private final List<Fragment> mFragments;
+ private final List<FragmentManagerNonConfig> mChildNonConfigs;
+
+ FragmentManagerNonConfig(List<Fragment> fragments,
+ List<FragmentManagerNonConfig> childNonConfigs) {
+ mFragments = fragments;
+ mChildNonConfigs = childNonConfigs;
+ }
+
+ /**
+ * @return the retained instance fragments returned by a FragmentManager
+ */
+ List<Fragment> getFragments() {
+ return mFragments;
+ }
+
+ /**
+ * @return the FragmentManagerNonConfigs from any applicable fragment's child FragmentManager
+ */
+ List<FragmentManagerNonConfig> getChildNonConfigs() {
+ return mChildNonConfigs;
+ }
+}