Add facility to switch to new fragments from preferences.
Change-Id: I009315b59cf81b4962e9c5a4490f0f82743ed64a
diff --git a/api/current.xml b/api/current.xml
index 859175e..76d8eed 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -133430,6 +133430,17 @@
visibility="public"
>
</method>
+<method name="getFragment"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getIntent"
return="android.content.Intent"
abstract="false"
@@ -133992,6 +134003,19 @@
<parameter name="enabled" type="boolean">
</parameter>
</method>
+<method name="setFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="java.lang.String">
+</parameter>
+</method>
<method name="setIntent"
return="void"
abstract="false"
@@ -134310,6 +134334,8 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback">
+</implements>
<constructor name="PreferenceActivity"
type="android.preference.PreferenceActivity"
static="false"
@@ -134455,6 +134481,21 @@
visibility="public"
>
</method>
+<method name="onPreferenceStartFragment"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="caller" type="android.preference.PreferenceFragment">
+</parameter>
+<parameter name="pref" type="android.preference.Preference">
+</parameter>
+</method>
<method name="onPreferenceTreeClick"
return="boolean"
abstract="false"
@@ -134678,6 +134719,29 @@
</parameter>
</method>
</class>
+<interface name="PreferenceFragment.OnPreferenceStartFragmentCallback"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onPreferenceStartFragment"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="caller" type="android.preference.PreferenceFragment">
+</parameter>
+<parameter name="pref" type="android.preference.Preference">
+</parameter>
+</method>
+</interface>
<class name="PreferenceGroup"
extends="android.preference.Preference"
abstract="true"
@@ -266144,7 +266208,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="fileName" type="java.lang.String">
+<parameter name="filename" type="java.lang.String">
</parameter>
</method>
</interface>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9beaec0..e3351b0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2055,9 +2055,10 @@
/**
* 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 that entry will also be
- * removed. Otherwise, all entries up to but not including that entry
- * will be removed
+ * 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;
@@ -2066,7 +2067,7 @@
* to pop, else false.
*/
public boolean popBackStack() {
- return popBackStack(null, 0);
+ return popBackStack(null, -1);
}
/**
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index c0e757d..2054e2a 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -629,7 +629,7 @@
if (mBackStack == null) {
return false;
}
- if (name == null && id < 0) {
+ if (name == null && id < 0 && (flags&Activity.POP_BACK_STACK_INCLUSIVE) == 0) {
int last = mBackStack.size()-1;
if (last < 0) {
return false;
@@ -644,22 +644,37 @@
}
});
} else {
- int index = mBackStack.size()-1;
- while (index >= 0) {
- BackStackEntry bss = mBackStack.get(index);
- if (name != null && name.equals(bss.getName())) {
- break;
+ int index = -1;
+ if (name != null || id >= 0) {
+ // If a name or ID is specified, look for that place in
+ // the stack.
+ index = mBackStack.size()-1;
+ while (index >= 0) {
+ BackStackEntry bss = mBackStack.get(index);
+ if (name != null && name.equals(bss.getName())) {
+ break;
+ }
+ if (id >= 0 && id == bss.mIndex) {
+ break;
+ }
+ index--;
}
- if (id >= 0 && id == bss.mIndex) {
- break;
+ if (index < 0) {
+ return false;
}
- index--;
- }
- if (index < 0) {
- return false;
- }
- if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) {
- index--;
+ if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) {
+ index--;
+ // Consume all following entries that match.
+ while (index >= 0) {
+ BackStackEntry bss = mBackStack.get(index);
+ if ((name != null && name.equals(bss.getName()))
+ || (id >= 0 && id == bss.mIndex)) {
+ index--;
+ continue;
+ }
+ break;
+ }
+ }
}
if (index == mBackStack.size()-1) {
return false;
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 381f794..117e507 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -56,6 +56,7 @@
* @attr ref android.R.styleable#Preference_title
* @attr ref android.R.styleable#Preference_summary
* @attr ref android.R.styleable#Preference_order
+ * @attr ref android.R.styleable#Preference_fragment
* @attr ref android.R.styleable#Preference_layout
* @attr ref android.R.styleable#Preference_widgetLayout
* @attr ref android.R.styleable#Preference_enabled
@@ -88,6 +89,7 @@
private CharSequence mSummary;
private String mKey;
private Intent mIntent;
+ private String mFragment;
private boolean mEnabled = true;
private boolean mSelectable = true;
private boolean mRequiresKey;
@@ -210,6 +212,10 @@
mOrder = a.getInt(attr, mOrder);
break;
+ case com.android.internal.R.styleable.Preference_fragment:
+ mFragment = a.getString(attr);
+ break;
+
case com.android.internal.R.styleable.Preference_layout:
mLayoutResId = a.getResourceId(attr, mLayoutResId);
break;
@@ -315,6 +321,24 @@
}
/**
+ * Sets the class name of a fragment to be shown when this Preference is clicked.
+ *
+ * @param fragment The class name of the fragment associated with this Preference.
+ */
+ public void setFragment(String fragment) {
+ mFragment = fragment;
+ }
+
+ /**
+ * Return the fragment class name associated with this Preference.
+ *
+ * @return The fragment class name last set via {@link #setFragment} or XML.
+ */
+ public String getFragment() {
+ return mFragment;
+ }
+
+ /**
* Sets the layout resource that is inflated as the {@link View} to be shown
* for this Preference. In most cases, the default layout is sufficient for
* custom Preference objects and only the widget layout needs to be changed.
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index fdc874b..e13c3e8 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -93,11 +93,22 @@
*
* {@sample development/samples/ApiDemos/res/xml/preference_headers.xml headers}
*
- * See {@link PreferenceFragment} for information on implementing the
+ * <p>The first header is shown by Prefs1Fragment, which populates itself
+ * from the following XML resource:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/xml/fragmented_preferences.xml preferences}
+ *
+ * <p>Note that this XML resource contains a preference screen holding another
+ * fragment, the Prefs1FragmentInner implemented here. This allows the user
+ * to traverse down a hierarchy of preferences; pressing back will pop each
+ * fragment off the stack to return to the previous preferences.
+ *
+ * <p>See {@link PreferenceFragment} for information on implementing the
* fragments themselves.
*/
public abstract class PreferenceActivity extends ListActivity implements
- PreferenceManager.OnPreferenceTreeClickListener {
+ PreferenceManager.OnPreferenceTreeClickListener,
+ PreferenceFragment.OnPreferenceStartFragmentCallback {
private static final String TAG = "PreferenceActivity";
private static final String PREFERENCES_TAG = "android:preferences";
@@ -106,6 +117,8 @@
private static final String EXTRA_PREFS_NO_HEADERS = ":android:no_headers";
+ private static final String BACK_STACK_PREFS = ":android:prefs";
+
// extras that allow any preference activity to be launched as part of a wizard
// show Back and Next buttons? takes boolean parameter
@@ -206,16 +219,19 @@
public static class Header {
/**
* Title of the header that is shown to the user.
+ * @attr ref android.R.styleable#PreferenceHeader_title
*/
CharSequence title;
/**
* Optional summary describing what this header controls.
+ * @attr ref android.R.styleable#PreferenceHeader_summary
*/
CharSequence summary;
/**
* Optional icon resource to show for this header.
+ * @attr ref android.R.styleable#PreferenceHeader_icon
*/
int iconRes;
@@ -228,6 +244,7 @@
/**
* Full class name of the fragment to display when this header is
* selected.
+ * @attr ref android.R.styleable#PreferenceHeader_fragment
*/
String fragment;
}
@@ -551,6 +568,8 @@
* @param fragmentName The name of the fragment to display.
*/
public void switchToHeader(String fragmentName) {
+ popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
+
Fragment f;
try {
f = Fragment.instantiate(this, fragmentName);
@@ -561,6 +580,20 @@
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;
+ }
+ openFragmentTransaction().replace(com.android.internal.R.id.prefs, f)
+ .addToBackStack(BACK_STACK_PREFS).commit();
+ return true;
+ }
+
/**
* Posts a message to bind the preferences to the list view.
* <p>
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index ac61574..a5395e2 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -125,6 +125,21 @@
}
};
+ /**
+ * Interface that PreferenceFragment's containing activity should
+ * implement to be able to process preference items that wish to
+ * switch to a new fragment.
+ */
+ public interface OnPreferenceStartFragmentCallback {
+ /**
+ * Called when the user has clicked on a Preference that has
+ * a fragment class name associated with it. The implementation
+ * to should instantiate and switch to an instance of the given
+ * fragment.
+ */
+ boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref);
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -168,6 +183,13 @@
}
@Override
+ public void onDestroyView() {
+ mList = null;
+ mHandler.removeCallbacks(mRequestFocus);
+ super.onDestroyView();
+ }
+
+ @Override
public void onDestroy() {
super.onDestroy();
mPreferenceManager.dispatchActivityDestroy();
@@ -251,7 +273,13 @@
/**
* {@inheritDoc}
*/
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+ Preference preference) {
+ if (preference.getFragment() != null &&
+ getActivity() instanceof OnPreferenceStartFragmentCallback) {
+ return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment(
+ this, preference);
+ }
return false;
}
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index 95e54324..f34f4a3 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -136,7 +136,7 @@
@Override
protected void onClick() {
- if (getIntent() != null || getPreferenceCount() == 0) {
+ if (getIntent() != null || getFragment() != null || getPreferenceCount() == 0) {
return;
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 728f999..97c5822 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3457,6 +3457,9 @@
<!-- The order for the Preference (lower values are to be ordered first). If this is not
specified, the default orderin will be alphabetic. -->
<attr name="order" format="integer" />
+ <!-- When used inside of a modern PreferenceActivity, this declares
+ a new PreferenceFragment to be shown when the user selects this item. -->
+ <attr name="fragment" />
<!-- The layout for the Preference in a PreferenceActivity screen. This should
rarely need to be changed, look at widgetLayout instead. -->
<attr name="layout" />