package android.preference;
+import android.animation.LayoutTransition;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.XmlRes;
* items. Doing this implicitly switches the class into its new "headers
* + fragments" mode rather than the old style of just showing a single
* preferences list.
- *
+ *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For information about using {@code PreferenceActivity},
private ViewGroup mPrefsContainer;
+ // Null if in legacy mode.
+ private ViewGroup mHeadersContainer;
+
private FragmentBreadCrumbs mFragmentBreadCrumbs;
private boolean mSinglePane;
holder = (HeaderViewHolder) view.getTag();
}
- // All view fields must be updated every time, because the view may be recycled
+ // All view fields must be updated every time, because the view may be recycled
Header header = getItem(position);
if (mRemoveIconIfEmpty) {
if (header.iconRes == 0) {
}
return breadCrumbShortTitle;
}
-
+
@Override
public int describeContents() {
return 0;
mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer);
mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs_frame);
+ mHeadersContainer = (ViewGroup) findViewById(com.android.internal.R.id.headers);
boolean hidingHeaders = onIsHidingHeaders();
mSinglePane = hidingHeaders || !onIsMultiPane();
String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
(int) HEADER_ID_UNDEFINED);
if (curHeader >= 0 && curHeader < mHeaders.size()) {
setSelectedHeader(mHeaders.get(curHeader));
+ } else if (!mSinglePane && initialFragment == null) {
+ switchToHeader(onGetInitialHeader());
}
+ } else {
+ // This will for instance hide breadcrumbs for single pane.
+ showBreadCrumbs(getTitle(), null);
}
-
} else {
- if (initialFragment != null && mSinglePane) {
- // If we are just showing a fragment, we want to run in
- // new fragment mode, but don't need to compute and show
- // the headers.
- switchToHeader(initialFragment, initialArguments);
- if (initialTitle != 0) {
- CharSequence initialTitleStr = getText(initialTitle);
- CharSequence initialShortTitleStr = initialShortTitle != 0
- ? getText(initialShortTitle) : null;
- showBreadCrumbs(initialTitleStr, initialShortTitleStr);
- }
-
- } else {
- // We need to try to build the headers.
+ if (!onIsHidingHeaders()) {
onBuildHeaders(mHeaders);
+ }
- // If there are headers, then at this point we need to show
- // them and, depending on the screen, we may also show in-line
- // the currently selected preference fragment.
- if (mHeaders.size() > 0) {
- if (!mSinglePane) {
- if (initialFragment == null) {
- Header h = onGetInitialHeader();
- switchToHeader(h);
- } else {
- switchToHeader(initialFragment, initialArguments);
- }
- }
- }
+ if (initialFragment != null) {
+ switchToHeader(initialFragment, initialArguments);
+ } else if (!mSinglePane && mHeaders.size() > 0) {
+ switchToHeader(onGetInitialHeader());
}
}
- // The default configuration is to only show the list view. Adjust
- // visibility for other configurations.
- if (initialFragment != null && mSinglePane) {
- // Single pane, showing just a prefs fragment.
- findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
- mPrefsContainer.setVisibility(View.VISIBLE);
- if (initialTitle != 0) {
- CharSequence initialTitleStr = getText(initialTitle);
- CharSequence initialShortTitleStr = initialShortTitle != 0
- ? getText(initialShortTitle) : null;
- showBreadCrumbs(initialTitleStr, initialShortTitleStr);
- }
- } else if (mHeaders.size() > 0) {
+ if (mHeaders.size() > 0) {
setListAdapter(new HeaderAdapter(this, mHeaders, mPreferenceHeaderItemResId,
mPreferenceHeaderRemoveEmptyIcon));
- if (!mSinglePane) {
- // Multi-pane.
- getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
- if (mCurHeader != null) {
- setSelectedHeader(mCurHeader);
- }
- mPrefsContainer.setVisibility(View.VISIBLE);
- }
- } else {
+ getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+ }
+
+ if (mSinglePane && initialFragment != null && initialTitle != 0) {
+ CharSequence initialTitleStr = getText(initialTitle);
+ CharSequence initialShortTitleStr = initialShortTitle != 0
+ ? getText(initialShortTitle) : null;
+ showBreadCrumbs(initialTitleStr, initialShortTitleStr);
+ }
+
+ if (mHeaders.size() == 0 && initialFragment == null) {
// If there are no headers, we are in the old "just show a screen
// of preferences" mode.
setContentView(com.android.internal.R.layout.preference_list_content_single);
mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs);
mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
mPreferenceManager.setOnPreferenceTreeClickListener(this);
+ mHeadersContainer = null;
+ } else if (mSinglePane) {
+ // Single-pane so one of the header or prefs containers must be hidden.
+ if (initialFragment != null || mCurHeader != null) {
+ mHeadersContainer.setVisibility(View.GONE);
+ } else {
+ mPrefsContainer.setVisibility(View.GONE);
+ }
+
+ // This animates our manual transitions between headers and prefs panel in single-pane.
+ // It also comes last so we don't animate any initial layout changes done above.
+ ViewGroup container = (ViewGroup) findViewById(
+ com.android.internal.R.id.prefs_container);
+ container.setLayoutTransition(new LayoutTransition());
+ } else {
+ // Multi-pane
+ if (mHeaders.size() > 0 && mCurHeader != null) {
+ setSelectedHeader(mCurHeader);
+ }
}
// see if we should show Back/Next buttons
}
}
+ @Override
+ public void onBackPressed() {
+ if (mCurHeader != null && mSinglePane && getFragmentManager().getBackStackEntryCount() == 0
+ && getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null) {
+ mCurHeader = null;
+
+ mPrefsContainer.setVisibility(View.GONE);
+ mHeadersContainer.setVisibility(View.VISIBLE);
+ getListView().clearChoices();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
/**
* Returns true if this activity is currently showing the header list.
*/
public boolean hasHeaders() {
- return getListView().getVisibility() == View.VISIBLE
- && mPreferenceManager == null;
+ return mHeadersContainer != null && mHeadersContainer.getVisibility() == View.VISIBLE;
}
/**
* and a preference fragment.
*/
public boolean isMultiPane() {
- return hasHeaders() && mPrefsContainer.getVisibility() == View.VISIBLE;
+ return !mSinglePane;
}
/**
if (!"preference-headers".equals(nodeName)) {
throw new RuntimeException(
"XML document must start with <preference-headers> tag; found"
- + nodeName + " at " + parser.getPositionDescription());
+ + nodeName + " at " + parser.getPositionDescription());
}
Bundle curBundle = null;
final int outerDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
final int innerDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.KITKAT) {
throw new RuntimeException(
"Subclasses of PreferenceActivity must override isValidFragment(String)"
- + " to verify that the Fragment class is valid! " + this.getClass().getName()
- + " has not checked if fragment " + fragmentName + " is valid.");
+ + " to verify that the Fragment class is valid! "
+ + this.getClass().getName()
+ + " has not checked if fragment " + fragmentName + " is valid.");
} else {
return true;
}
// Only call this if we didn't save the instance state for later.
// If we did save it, it will be restored when we bind the adapter.
super.onRestoreInstanceState(state);
+
+ if (!mSinglePane) {
+ // Multi-pane.
+ if (mCurHeader != null) {
+ setSelectedHeader(mCurHeader);
+ }
+ }
}
@Override
*/
public void onHeaderClick(Header header, int position) {
if (header.fragment != null) {
- if (mSinglePane) {
- int titleRes = header.breadCrumbTitleRes;
- int shortTitleRes = header.breadCrumbShortTitleRes;
- if (titleRes == 0) {
- titleRes = header.titleRes;
- shortTitleRes = 0;
- }
- startWithFragment(header.fragment, header.fragmentArguments, null, 0,
- titleRes, shortTitleRes);
- } else {
- switchToHeader(header);
- }
+ switchToHeader(header);
} else if (header.intent != null) {
startActivity(header.intent);
}
* the selected fragment. The default implementation constructs an Intent
* that re-launches the current activity with the appropriate arguments to
* display the fragment.
- *
+ *
* @param fragmentName The name of the fragment to display.
* @param args Optional arguments to supply to the fragment.
* @param titleRes Optional resource ID of title to show for this item.
intent.putExtra(EXTRA_NO_HEADERS, true);
return intent;
}
-
+
/**
* Like {@link #startWithFragment(String, Bundle, Fragment, int, int, int)}
* but uses a 0 titleRes.
throw new IllegalArgumentException("Invalid fragment for this activity: "
+ fragmentName);
}
+
Fragment f = Fragment.instantiate(this, fragmentName, args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
+ transaction.setTransition(mSinglePane
+ ? FragmentTransaction.TRANSIT_NONE
+ : FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.replace(com.android.internal.R.id.prefs, f);
transaction.commitAllowingStateLoss();
+
+ if (mSinglePane && mPrefsContainer.getVisibility() == View.GONE) {
+ // We are transitioning from headers to preferences panel in single-pane so we need
+ // to hide headers and show the prefs container.
+ mPrefsContainer.setVisibility(View.VISIBLE);
+ mHeadersContainer.setVisibility(View.GONE);
+ }
}
/**
* be instantiated and placed in the appropriate pane. If running in
* single-pane mode, a new activity will be launched in which to show the
* fragment.
- *
+ *
* @param fragmentClass Full name of the class implementing the fragment.
* @param args Any desired arguments to supply to the fragment.
* @param titleRes Optional resource identifier of the title of this
*/
public void startPreferencePanel(String fragmentClass, Bundle args, @StringRes int titleRes,
CharSequence titleText, Fragment resultTo, int resultRequestCode) {
- if (mSinglePane) {
- startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, 0);
- } else {
- Fragment f = Fragment.instantiate(this, fragmentClass, args);
- if (resultTo != null) {
- f.setTargetFragment(resultTo, resultRequestCode);
- }
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.replace(com.android.internal.R.id.prefs, f);
- if (titleRes != 0) {
- transaction.setBreadCrumbTitle(titleRes);
- } else if (titleText != null) {
- transaction.setBreadCrumbTitle(titleText);
- }
- transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
- transaction.addToBackStack(BACK_STACK_PREFS);
- transaction.commitAllowingStateLoss();
+ Fragment f = Fragment.instantiate(this, fragmentClass, args);
+ if (resultTo != null) {
+ f.setTargetFragment(resultTo, resultRequestCode);
}
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.replace(com.android.internal.R.id.prefs, f);
+ if (titleRes != 0) {
+ transaction.setBreadCrumbTitle(titleRes);
+ } else if (titleText != null) {
+ transaction.setBreadCrumbTitle(titleText);
+ }
+ transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+ transaction.addToBackStack(BACK_STACK_PREFS);
+ transaction.commitAllowingStateLoss();
}
/**
* Called by a preference panel fragment to finish itself.
- *
+ *
* @param caller The fragment that is asking to be finished.
* @param resultCode Optional result code to send back to the original
* launching fragment.
* launching fragment.
*/
public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
- if (mSinglePane) {
- setResult(resultCode, resultData);
- finish();
- } else {
- // XXX be smarter about popping the stack.
- onBackPressed();
- if (caller != null) {
- if (caller.getTargetFragment() != null) {
- caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(),
- resultCode, resultData);
- }
+ // TODO: be smarter about popping the stack.
+ onBackPressed();
+ if (caller != null) {
+ if (caller.getTargetFragment() != null) {
+ caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(),
+ resultCode, resultData);
}
}
}
-
+
@Override
public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
startPreferencePanel(pref.getFragment(), pref.getExtras(), pref.getTitleRes(),