From b7a2e4772220c4b41df1260cedaf8912f4b07547 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 12 Aug 2010 16:20:42 -0700 Subject: [PATCH] Fragment and PreferenceFragment and FragmentManager, oh my! - Introduce FragmentManager public API, for all Fragment management needs. Will in the future allow the removal of the (growing number of) fragment APIs on Activity. - Fragment now has a concept of arguments. This can be supplied immediately after creation, and are retained across instances. - PreferenceActivity now has an API to have it update its headers (note not tested). Headers now have arguments. Keys for controlling when PreferenceActivity shows at launch have been added to the SDK. - Fixes to back stack handling and state saving/restoring. Change-Id: Ib9d07ae2beb296c4eb3a4d9e1b3b59544675e819 --- api/current.xml | 328 ++++++++++++++++-- core/java/android/app/Activity.java | 83 ++--- core/java/android/app/BackStackEntry.java | 108 +++--- core/java/android/app/DialogFragment.java | 7 +- core/java/android/app/Fragment.java | 120 +++++-- core/java/android/app/FragmentManager.java | 166 ++++++++- .../preference/PreferenceActivity.java | 102 ++++-- core/java/android/util/AndroidException.java | 4 + .../android/util/AndroidRuntimeException.java | 4 + 9 files changed, 730 insertions(+), 192 deletions(-) diff --git a/api/current.xml b/api/current.xml index 3535ffc58a4fa..b0056d7f22243 100644 --- a/api/current.xml +++ b/api/current.xml @@ -21946,6 +21946,17 @@ visibility="public" > + + - - - - - - + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + @@ -179210,6 +179474,18 @@ deprecated="not deprecated" visibility="public" > + + + + + + diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d7bab1bc80806..ec3cbc3729c4c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -667,7 +667,7 @@ public class Activity extends ContextThemeWrapper private CharSequence mTitle; private int mTitleColor = 0; - final FragmentManager mFragments = new FragmentManager(); + final FragmentManagerImpl mFragments = new FragmentManagerImpl(); SparseArray mAllLoaderManagers; LoaderManagerImpl mLoaderManager; @@ -1579,12 +1579,20 @@ public class Activity extends ContextThemeWrapper mCalled = true; } + /** + * Return the FragmentManager for interacting with fragments associated + * with this activity. + */ + public FragmentManager getFragmentManager() { + return mFragments; + } + /** * Start a series of edit operations on the Fragments associated with * this activity. */ public FragmentTransaction openFragmentTransaction() { - return new BackStackEntry(mFragments); + return mFragments.openTransaction(); } void invalidateFragmentIndex(int index) { @@ -2072,7 +2080,7 @@ public class Activity extends ContextThemeWrapper * to pop, else false. */ public boolean popBackStack() { - return popBackStack(null, 0); + return mFragments.popBackStack(); } /** @@ -2085,7 +2093,7 @@ public class Activity extends ContextThemeWrapper * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. */ public boolean popBackStack(String name, int flags) { - return mFragments.popBackStackState(mHandler, name, flags); + return mFragments.popBackStack(name, flags); } /** @@ -2099,7 +2107,7 @@ public class Activity extends ContextThemeWrapper * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. */ public boolean popBackStack(int id, int flags) { - return mFragments.popBackStackState(mHandler, id, flags); + return mFragments.popBackStack(id, flags); } /** @@ -3999,43 +4007,36 @@ public class Activity extends ContextThemeWrapper + ": Must specify unique android:id for " + fname); } - try { - // 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 (FragmentManager.DEBUG) Log.v(TAG, "onCreateView: id=0x" - + Integer.toHexString(id) + " fname=" + fname - + " existing=" + fragment); - if (fragment == null) { - fragment = Fragment.instantiate(this, fname); - fragment.mFromLayout = true; - fragment.mFragmentId = id; - fragment.mTag = tag; - fragment.mImmediateActivity = this; - mFragments.addFragment(fragment, true); - } - // If this fragment is newly instantiated (either right now, or - // from last saved state), then give it the attributes to - // initialize itself. - if (!fragment.mRetaining) { - fragment.onInflate(this, attrs, fragment.mSavedFragmentState); - } - 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 (Exception e) { - InflateException ie = new InflateException(attrs.getPositionDescription() - + ": Error inflating fragment " + fname); - ie.initCause(e); - throw ie; + // 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 (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x" + + Integer.toHexString(id) + " fname=" + fname + + " existing=" + fragment); + if (fragment == null) { + fragment = Fragment.instantiate(this, fname); + fragment.mFromLayout = true; + fragment.mFragmentId = id; + fragment.mTag = tag; + fragment.mImmediateActivity = this; + mFragments.addFragment(fragment, true); } + // If this fragment is newly instantiated (either right now, or + // from last saved state), then give it the attributes to + // initialize itself. + if (!fragment.mRetaining) { + fragment.onInflate(this, attrs, fragment.mSavedFragmentState); + } + 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; } /** diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackEntry.java index d63b862ac3d87..520e4fd586dd3 100644 --- a/core/java/android/app/BackStackEntry.java +++ b/core/java/android/app/BackStackEntry.java @@ -29,7 +29,7 @@ final class BackStackState implements Parcelable { final String mName; final int mIndex; - public BackStackState(FragmentManager fm, BackStackEntry bse) { + public BackStackState(FragmentManagerImpl fm, BackStackEntry bse) { int numRemoved = 0; BackStackEntry.Op op = bse.mHead; while (op != null) { @@ -38,6 +38,10 @@ final class BackStackState implements Parcelable { } mOps = new int[bse.mNumOp*5 + numRemoved]; + if (!bse.mAddToBackStack) { + throw new IllegalStateException("Not on back stack"); + } + op = bse.mHead; int pos = 0; while (op != null) { @@ -70,14 +74,15 @@ final class BackStackState implements Parcelable { mIndex = in.readInt(); } - public BackStackEntry instantiate(FragmentManager fm) { + public BackStackEntry instantiate(FragmentManagerImpl fm) { BackStackEntry bse = new BackStackEntry(fm); int pos = 0; while (pos < mOps.length) { BackStackEntry.Op op = new BackStackEntry.Op(); op.cmd = mOps[pos++]; + if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, + "BSE " + bse + " set base fragment #" + mOps[pos]); Fragment f = fm.mActive.get(mOps[pos++]); - f.mBackStackNesting++; op.fragment = f; op.enterAnim = mOps[pos++]; op.exitAnim = mOps[pos++]; @@ -85,7 +90,10 @@ final class BackStackState implements Parcelable { if (N > 0) { op.removed = new ArrayList(N); for (int i=0; i=0; i--) { + Fragment r = op.removed.get(i); + r.mBackStackNesting += amt; + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " + + r + " to " + r.mBackStackNesting); + } + } + op = op.next; + } + } + public int commit() { if (mCommitted) throw new IllegalStateException("commit already called"); - if (FragmentManager.DEBUG) Log.v(TAG, "Commit: " + this); + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this); mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this); @@ -309,7 +342,7 @@ final class BackStackEntry implements FragmentTransaction, Runnable { } public void run() { - if (FragmentManager.DEBUG) Log.v(TAG, "Run: " + this); + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this); if (mAddToBackStack) { if (mIndex < 0) { @@ -317,14 +350,13 @@ final class BackStackEntry implements FragmentTransaction, Runnable { } } + bumpBackStackNesting(1); + Op op = mHead; while (op != null) { switch (op.cmd) { case OP_ADD: { Fragment f = op.fragment; - if (mAddToBackStack) { - f.mBackStackNesting++; - } f.mNextAnim = op.enterAnim; mManager.addFragment(f, false); } break; @@ -333,48 +365,38 @@ final class BackStackEntry implements FragmentTransaction, Runnable { if (mManager.mAdded != null) { for (int i=0; iEvery fragment must have an * empty constructor, so it can be instantiated when restoring its * activity's state. It is strongly recommended that subclasses do not * have other constructors with parameters, since these constructors * will not be called when the fragment is re-instantiated; instead, + * arguments can be supplied by the caller with {@link #setArguments} + * and later retrieved by the Fragment with {@link #getArguments}. + * + *

The first place where application code should generally run is in + * {@link #onAttach(Activity)}, which is the point where the fragment is + * actually attached to its activity and thus capable of doing most * retrieve such parameters from the activity in {@link #onAttach(Activity)}. */ public Fragment() { } + /** + * Like {@link #instantiate(Context, String, Bundle)} but with a null + * argument Bundle. + */ + public static Fragment instantiate(Context context, String fname) { + return instantiate(context, fname, null); + } + /** * Create a new instance of a Fragment with the given class name. This is * the same as calling its empty constructor. @@ -237,29 +264,40 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener * @param context The calling context being used to instantiate the fragment. * This is currently just used to get its ClassLoader. * @param fname The class name of the fragment to instantiate. + * @param args Bundle of arguments to supply to the fragment, which it + * can retrieve with {@link #getArguments()}. May be null. * @return Returns a new fragment instance. - * @throws NoSuchMethodException The fragment does not have an empty constructor. - * @throws ClassNotFoundException The fragment class does not exist. - * @throws IllegalArgumentException Bad arguments supplied to fragment class - * constructor (should not happen). - * @throws InstantiationException Caller does not have permission to instantiate - * the fragment (for example its constructor is not public). - * @throws IllegalAccessException Caller does not have permission to access - * the given fragment class. - * @throws InvocationTargetException Failure running the fragment's constructor. + * @throws InstantiationException If there is a failure in instantiating + * the given fragment class. This is a runtime exception; it is not + * normally expected to happen. */ - public static Fragment instantiate(Context context, String fname) - throws NoSuchMethodException, ClassNotFoundException, - IllegalArgumentException, InstantiationException, - IllegalAccessException, InvocationTargetException { - Class clazz = sClassMap.get(fname); - - if (clazz == null) { - // Class not found in the cache, see if it's real, and try to add it - clazz = context.getClassLoader().loadClass(fname); - sClassMap.put(fname, clazz); + public static Fragment instantiate(Context context, String fname, Bundle args) { + try { + Class clazz = sClassMap.get(fname); + if (clazz == null) { + // Class not found in the cache, see if it's real, and try to add it + clazz = context.getClassLoader().loadClass(fname); + sClassMap.put(fname, clazz); + } + Fragment f = (Fragment)clazz.newInstance(); + if (args != null) { + args.setClassLoader(f.getClass().getClassLoader()); + f.mArguments = args; + } + return f; + } catch (ClassNotFoundException e) { + throw new InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } catch (java.lang.InstantiationException e) { + throw new InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } catch (IllegalAccessException e) { + throw new InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); } - return (Fragment)clazz.newInstance(); } void restoreViewState() { @@ -330,6 +368,28 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener return mTag; } + /** + * Supply the construction arguments for this fragment. This can only + * be called before the fragment has been attached to its activity; that + * is, you should call it immediately after constructing the fragment. The + * arguments supplied here will be retained across fragment destroy and + * creation. + */ + final public void setArguments(Bundle args) { + if (mIndex >= 0) { + throw new IllegalStateException("Fragment already active"); + } + mArguments = args; + } + + /** + * Return the arguments supplied when the fragment was instantiated, + * if any. + */ + final public Bundle getArguments() { + return mArguments; + } + /** * Return the Activity this fragment is currently associated with. */ @@ -337,6 +397,14 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener return mActivity; } + /** + * Return the FragmentManager for interacting with fragments associated + * with this fragment's activity. + */ + final public FragmentManager getFragmentManager() { + return mActivity.mFragments; + } + /** * Return true if the fragment is currently added to its activity. */ diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 54e37b0df5738..0556f0592d1e0 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -35,6 +35,100 @@ import android.view.animation.AnimationUtils; import java.util.ArrayList; +/** + * Interface for interacting with {@link Fragment} objects inside of an + * {@link Activity} + */ +public interface FragmentManager { + /** + * Start a series of edit operations on the Fragments associated with + * this FragmentManager. + */ + public FragmentTransaction openTransaction(); + + /** + * 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 first + * searches through fragments that are currently added to the manager's + * activity; if no such fragment is found, then all fragments currently + * on the back stack associated with this ID are searched. + * @return The fragment if found or null otherwise. + */ + public Fragment findFragmentById(int 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 first + * searches through fragments that are currently added to the manager's + * activity; if no such fragment is found, then all fragments currently + * on the back stack are searched. + * @return The fragment if found or null otherwise. + */ + public Fragment findFragmentByTag(String tag); + + /** + * Flag for {@link #popBackStack(String, int)} + * and {@link #popBackStack(int, int)}: If set, and the name or ID of + * a back stack entry has been supplied, then all matching entries will + * be consumed until one that doesn't match is found or the bottom of + * the stack is reached. Otherwise, all entries up to but not including that entry + * will be removed. + */ + public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; + + /** + * Pop the top state off the back stack. Returns true if there was one + * to pop, else false. + */ + public boolean popBackStack(); + + /** + * Pop the last fragment transition from the manager's fragment + * back stack. If there is nothing to pop, false is returned. + * @param name If non-null, this is the name of a previous back state + * to look for; if found, all states up to that state will be popped. The + * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether + * the named state itself is popped. If null, only the top state is popped. + * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. + */ + public boolean popBackStack(String name, int flags); + + /** + * Pop all back stack states up to the one with the given identifier. + * @param id Identifier of the stated to be popped. If no identifier exists, + * false is returned. + * The identifier is the number returned by + * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The + * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether + * the named state itself is popped. + * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. + */ + public boolean popBackStack(int id, int flags); + + /** + * Put a reference to a fragment in a Bundle. This Bundle can be + * persisted as saved state, and when later restoring + * {@link #getFragment(Bundle, String)} will return the current + * instance of the same fragment. + * + * @param bundle The bundle in which to put the fragment reference. + * @param key The name of the entry in the bundle. + * @param fragment The Fragment whose reference is to be stored. + */ + public void putFragment(Bundle bundle, String key, Fragment fragment); + + /** + * Retrieve the current Fragment instance for a reference previously + * placed with {@link #putFragment(Bundle, String, Fragment)}. + * + * @param bundle The bundle from which to retrieve the fragment reference. + * @param key The name of the entry in the bundle. + * @return Returns the current Fragment instance that is associated with + * the given reference. + */ + public Fragment getFragment(Bundle bundle, String key); +} + final class FragmentManagerState implements Parcelable { FragmentState[] mActive; int[] mAdded; @@ -75,7 +169,7 @@ final class FragmentManagerState implements Parcelable { * @hide * Container for fragments associated with an activity. */ -public class FragmentManager { +class FragmentManagerImpl implements FragmentManager { static final boolean DEBUG = true; static final String TAG = "FragmentManager"; @@ -108,6 +202,47 @@ public class FragmentManager { } }; + public FragmentTransaction openTransaction() { + return new BackStackEntry(this); + } + + public boolean popBackStack() { + return popBackStackState(mActivity.mHandler, null, -1, 0); + } + + public boolean popBackStack(String name, int flags) { + return popBackStackState(mActivity.mHandler, name, -1, flags); + } + + public boolean popBackStack(int id, int flags) { + if (id < 0) { + throw new IllegalArgumentException("Bad id: " + id); + } + return popBackStackState(mActivity.mHandler, null, id, flags); + } + + public void putFragment(Bundle bundle, String key, Fragment fragment) { + if (fragment.mIndex < 0) { + throw new IllegalStateException("Fragment " + fragment + + " is not currently in the FragmentManager"); + } + bundle.putInt(key, fragment.mIndex); + } + + public Fragment getFragment(Bundle bundle, String key) { + int index = bundle.getInt(key); + if (index >= mActive.size()) { + throw new IllegalStateException("Fragement no longer exists for key " + + key + ": index " + index); + } + Fragment f = mActive.get(index); + if (f == null) { + throw new IllegalStateException("Fragement no longer exists for key " + + key + ": index " + index); + } + return f; + } + Animatable loadAnimatable(Fragment fragment, int transit, boolean enter, int transitionStyle) { Animatable animObj = fragment.onCreateAnimatable(transit, enter, @@ -387,6 +522,7 @@ public class FragmentManager { return; } + if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex); mActive.set(f.mIndex, null); if (mAvailIndices == null) { mAvailIndices = new ArrayList(); @@ -636,17 +772,6 @@ public class FragmentManager { mBackStack.add(state); } - public boolean popBackStackState(Handler handler, String name, int flags) { - return popBackStackState(handler, name, -1, flags); - } - - public boolean popBackStackState(Handler handler, int id, int flags) { - if (id < 0) { - return false; - } - return popBackStackState(handler, null, id, flags); - } - boolean popBackStackState(Handler handler, String name, int id, int flags) { if (mBackStack == null) { return false; @@ -787,10 +912,13 @@ public class FragmentManager { } } + if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " + + fs.mSavedFragmentState); } } if (!haveFragments) { + if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); return null; } @@ -803,6 +931,8 @@ public class FragmentManager { added = new int[N]; for (int i=0; i(); } + if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i); mAvailIndices.add(i); } } @@ -878,6 +1015,7 @@ public class FragmentManager { } f.mAdded = true; f.mImmediateActivity = mActivity; + if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f); mAdded.add(f); } } else { @@ -889,6 +1027,8 @@ public class FragmentManager { mBackStack = new ArrayList(fms.mBackStack.length); for (int i=0; i= 0) { setBackStackIndex(bse.mIndex, bse); diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 114f67dc7a831..ec2ca57bca12e 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -113,9 +113,28 @@ public abstract class PreferenceActivity extends ListActivity implements private static final String PREFERENCES_TAG = "android:preferences"; - private static final String EXTRA_PREFS_SHOW_FRAGMENT = ":android:show_fragment"; + /** + * When starting this activity, the invoking Intent can contain this extra + * string to specify which fragment should be initially displayed. + */ + public static final String EXTRA_SHOW_FRAGMENT = ":android:show_fragment"; - private static final String EXTRA_PREFS_NO_HEADERS = ":android:no_headers"; + /** + * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, + * this extra can also be specify to supply a Bundle of arguments to pass + * to that fragment when it is instantiated during the initial creation + * of PreferenceActivity. + */ + public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":android:show_fragment_args"; + + /** + * When starting this activity, the invoking Intent can contain this extra + * boolean that the header list should not be displayed. This is most often + * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch + * the activity to display a specific fragment that the user has navigated + * to. + */ + public static final String EXTRA_NO_HEADERS = ":android:no_headers"; private static final String BACK_STACK_PREFS = ":android:prefs"; @@ -159,14 +178,18 @@ public abstract class PreferenceActivity extends ListActivity implements 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 Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_BIND_PREFERENCES: bindPreferences(); break; + case MSG_BUILD_HEADERS: + onBuildHeaders(mHeaders); + mAdapter.notifyDataSetChanged(); + break; } } }; @@ -250,6 +273,12 @@ public abstract class PreferenceActivity extends ListActivity implements * @attr ref android.R.styleable#PreferenceHeader_fragment */ String fragment; + + /** + * Optional arguments to supply to the fragment when it is + * instantiated. + */ + Bundle fragmentArguments; } @Override @@ -261,7 +290,8 @@ public abstract class PreferenceActivity extends ListActivity implements mPrefsContainer = findViewById(com.android.internal.R.id.prefs); boolean hidingHeaders = onIsHidingHeaders(); mSinglePane = hidingHeaders || !onIsMultiPane(); - String initialFragment = getIntent().getStringExtra(EXTRA_PREFS_SHOW_FRAGMENT); + String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT); + Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); if (initialFragment != null && mSinglePane) { // If we are just showing a fragment, we want to run in @@ -269,7 +299,7 @@ public abstract class PreferenceActivity extends ListActivity implements // the headers. getListView().setVisibility(View.GONE); mPrefsContainer.setVisibility(View.VISIBLE); - switchToHeader(initialFragment); + switchToHeader(initialFragment, initialArguments); } else { // We need to try to build the headers. @@ -283,8 +313,12 @@ public abstract class PreferenceActivity extends ListActivity implements setListAdapter(mAdapter); if (!mSinglePane) { mPrefsContainer.setVisibility(View.VISIBLE); - switchToHeader(initialFragment != null - ? initialFragment : onGetInitialFragment()); + if (initialFragment != null) { + Header h = onGetInitialHeader(); + initialFragment = h.fragment; + initialArguments = h.fragmentArguments; + } + switchToHeader(initialFragment, initialArguments); } // If there are no headers, we are in the old "just show a screen @@ -371,15 +405,18 @@ public abstract class PreferenceActivity extends ListActivity implements * when not in multi-pane mode. */ public boolean onIsHidingHeaders() { - return getIntent().getBooleanExtra(EXTRA_PREFS_NO_HEADERS, false); + return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false); } /** - * Called to determine the initial fragment to be shown. The default - * implementation simply returns the fragment of the first header. + * Called to determine the initial header to be shown. The default + * implementation simply returns the fragment of the first header. Note + * that the returned Header object does not actually need to exist in + * your header list -- whatever its fragment is will simply be used to + * show for the initial UI. */ - public String onGetInitialFragment() { - return mHeaders.get(0).fragment; + public Header onGetInitialHeader() { + return mHeaders.get(0); } /** @@ -398,6 +435,16 @@ public abstract class PreferenceActivity extends ListActivity implements public void onBuildHeaders(List

target) { } + /** + * Call when you need to change the headers being displayed. Will result + * in onBuildHeaders() later being called to retrieve the new list. + */ + public void invalidateHeaders() { + if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) { + mHandler.sendEmptyMessage(MSG_BUILD_HEADERS); + } + } + /** * Parse the given XML file as a header description, adding each * parsed Header into the target list. @@ -552,9 +599,9 @@ public abstract class PreferenceActivity extends ListActivity implements */ public void onHeaderClick(Header header, int position) { if (mSinglePane) { - startWithFragment(header.fragment); + startWithFragment(header.fragment, header.fragmentArguments); } else { - switchToHeader(header.fragment); + switchToHeader(header.fragment, header.fragmentArguments); } } @@ -565,12 +612,14 @@ public abstract class PreferenceActivity extends ListActivity implements * and fill the entire activity. * * @param fragmentName The name of the fragment to display. + * @param args Optional arguments to supply to the fragment. */ - public void startWithFragment(String fragmentName) { + public void startWithFragment(String fragmentName, Bundle args) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.setClass(this, getClass()); - intent.putExtra(EXTRA_PREFS_SHOW_FRAGMENT, fragmentName); - intent.putExtra(EXTRA_PREFS_NO_HEADERS, true); + intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName); + intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); + intent.putExtra(EXTRA_NO_HEADERS, true); startActivity(intent); } @@ -579,29 +628,18 @@ public abstract class PreferenceActivity extends ListActivity implements * preference fragment. * * @param fragmentName The name of the fragment to display. + * @param args Optional arguments to supply to the fragment. */ - public void switchToHeader(String fragmentName) { + public void switchToHeader(String fragmentName, Bundle args) { popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE); - Fragment f; - try { - f = Fragment.instantiate(this, fragmentName); - } catch (Exception e) { - Log.w(TAG, "Failure instantiating fragment " + fragmentName, e); - return; - } + Fragment f = Fragment.instantiate(this, fragmentName, args); openFragmentTransaction().replace(com.android.internal.R.id.prefs, f).commit(); } @Override public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { - Fragment f; - try { - f = Fragment.instantiate(this, pref.getFragment()); - } catch (Exception e) { - Log.w(TAG, "Failure instantiating fragment " + pref.getFragment(), e); - return false; - } + Fragment f = Fragment.instantiate(this, pref.getFragment()); openFragmentTransaction().replace(com.android.internal.R.id.prefs, f) .addToBackStack(BACK_STACK_PREFS).commit(); return true; diff --git a/core/java/android/util/AndroidException.java b/core/java/android/util/AndroidException.java index a767ea1e975c2..dfe00c9bd49fb 100644 --- a/core/java/android/util/AndroidException.java +++ b/core/java/android/util/AndroidException.java @@ -27,6 +27,10 @@ public class AndroidException extends Exception { super(name); } + public AndroidException(String name, Throwable cause) { + super(name, cause); + } + public AndroidException(Exception cause) { super(cause); } diff --git a/core/java/android/util/AndroidRuntimeException.java b/core/java/android/util/AndroidRuntimeException.java index 4ed17bcd31c6c..2b824bf9cb2ae 100644 --- a/core/java/android/util/AndroidRuntimeException.java +++ b/core/java/android/util/AndroidRuntimeException.java @@ -27,6 +27,10 @@ public class AndroidRuntimeException extends RuntimeException { super(name); } + public AndroidRuntimeException(String name, Throwable cause) { + super(name, cause); + } + public AndroidRuntimeException(Exception cause) { super(cause); }