PreferenceActivity.java revision 5c769a47aeb399324cca7f5d449331a3bba85b52
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.preference; 18 19import com.android.internal.util.XmlUtils; 20 21import org.xmlpull.v1.XmlPullParser; 22import org.xmlpull.v1.XmlPullParserException; 23 24import android.app.Fragment; 25import android.app.ListActivity; 26import android.content.Context; 27import android.content.Intent; 28import android.content.res.Configuration; 29import android.content.res.TypedArray; 30import android.content.res.XmlResourceParser; 31import android.graphics.drawable.Drawable; 32import android.os.Bundle; 33import android.os.Handler; 34import android.os.Message; 35import android.text.TextUtils; 36import android.util.AttributeSet; 37import android.util.Log; 38import android.util.Xml; 39import android.view.LayoutInflater; 40import android.view.View; 41import android.view.ViewGroup; 42import android.view.View.OnClickListener; 43import android.widget.ArrayAdapter; 44import android.widget.Button; 45import android.widget.FrameLayout; 46import android.widget.ImageView; 47import android.widget.ListView; 48import android.widget.TextView; 49 50import java.io.IOException; 51import java.util.ArrayList; 52import java.util.List; 53 54/** 55 * This is the base class for an activity to show a hierarchy of preferences 56 * to the user. Prior to {@link android.os.Build.VERSION_CODES#HONEYCOMB} 57 * this class only allowed the display of a single set of preference; this 58 * functionality should now be found in the new {@link PreferenceFragment} 59 * class. If you are using PreferenceActivity in its old mode, the documentation 60 * there applies to the deprecated APIs here. 61 * 62 * <p>This activity shows one or more headers of preferences, each of with 63 * is associated with a {@link PreferenceFragment} to display the preferences 64 * of that header. The actual layout and display of these associations can 65 * however vary; currently there are two major approaches it may take: 66 * 67 * <ul> 68 * <li>On a small screen it may display only the headers as a single list 69 * when first launched. Selecting one of the header items will re-launch 70 * the activity with it only showing the PreferenceFragment of that header. 71 * <li>On a large screen in may display both the headers and current 72 * PreferenceFragment together as panes. Selecting a header item switches 73 * to showing the correct PreferenceFragment for that item. 74 * </ul> 75 * 76 * <p>Subclasses of PreferenceActivity should implement 77 * {@link #onBuildHeaders} to populate the header list with the desired 78 * items. Doing this implicitly switches the class into its new "headers 79 * + fragments" mode rather than the old style of just showing a single 80 * preferences list. 81 * 82 * <a name="SampleCode"></a> 83 * <h3>Sample Code</h3> 84 * 85 * <p>The following sample code shows a simple preference activity that 86 * has two different sets of preferences. The implementation, consisting 87 * of the activity itself as well as its two preference fragments is:</p> 88 * 89 * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/PreferenceWithHeaders.java 90 * activity} 91 * 92 * <p>The preference_headers resource describes the headers to be displayed 93 * and the fragments associated with them. It is: 94 * 95 * {@sample development/samples/ApiDemos/res/xml/preference_headers.xml headers} 96 * 97 * <p>The first header is shown by Prefs1Fragment, which populates itself 98 * from the following XML resource:</p> 99 * 100 * {@sample development/samples/ApiDemos/res/xml/fragmented_preferences.xml preferences} 101 * 102 * <p>Note that this XML resource contains a preference screen holding another 103 * fragment, the Prefs1FragmentInner implemented here. This allows the user 104 * to traverse down a hierarchy of preferences; pressing back will pop each 105 * fragment off the stack to return to the previous preferences. 106 * 107 * <p>See {@link PreferenceFragment} for information on implementing the 108 * fragments themselves. 109 */ 110public abstract class PreferenceActivity extends ListActivity implements 111 PreferenceManager.OnPreferenceTreeClickListener, 112 PreferenceFragment.OnPreferenceStartFragmentCallback { 113 private static final String TAG = "PreferenceActivity"; 114 115 private static final String PREFERENCES_TAG = "android:preferences"; 116 117 /** 118 * When starting this activity, the invoking Intent can contain this extra 119 * string to specify which fragment should be initially displayed. 120 */ 121 public static final String EXTRA_SHOW_FRAGMENT = ":android:show_fragment"; 122 123 /** 124 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, 125 * this extra can also be specify to supply a Bundle of arguments to pass 126 * to that fragment when it is instantiated during the initial creation 127 * of PreferenceActivity. 128 */ 129 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":android:show_fragment_args"; 130 131 /** 132 * When starting this activity, the invoking Intent can contain this extra 133 * boolean that the header list should not be displayed. This is most often 134 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch 135 * the activity to display a specific fragment that the user has navigated 136 * to. 137 */ 138 public static final String EXTRA_NO_HEADERS = ":android:no_headers"; 139 140 private static final String BACK_STACK_PREFS = ":android:prefs"; 141 142 // extras that allow any preference activity to be launched as part of a wizard 143 144 // show Back and Next buttons? takes boolean parameter 145 // Back will then return RESULT_CANCELED and Next RESULT_OK 146 private static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar"; 147 148 // add a Skip button? 149 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip"; 150 151 // specify custom text for the Back or Next buttons, or cause a button to not appear 152 // at all by setting it to null 153 private static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text"; 154 private static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text"; 155 156 // --- State for new mode when showing a list of headers + prefs fragment 157 158 private final ArrayList<Header> mHeaders = new ArrayList<Header>(); 159 160 private HeaderAdapter mAdapter; 161 162 private FrameLayout mListFooter; 163 164 private View mPrefsContainer; 165 166 private boolean mSinglePane; 167 168 // --- State for old mode when showing a single preference list 169 170 private PreferenceManager mPreferenceManager; 171 172 private Bundle mSavedInstanceState; 173 174 // --- Common state 175 176 private Button mNextButton; 177 178 /** 179 * The starting request code given out to preference framework. 180 */ 181 private static final int FIRST_REQUEST_CODE = 100; 182 183 private static final int MSG_BIND_PREFERENCES = 0; 184 private static final int MSG_BUILD_HEADERS = 1; 185 private Handler mHandler = new Handler() { 186 @Override 187 public void handleMessage(Message msg) { 188 switch (msg.what) { 189 case MSG_BIND_PREFERENCES: 190 bindPreferences(); 191 break; 192 case MSG_BUILD_HEADERS: 193 onBuildHeaders(mHeaders); 194 mAdapter.notifyDataSetChanged(); 195 Header header = onGetNewHeader(); 196 if (header != null && header.fragment != null) { 197 switchToHeader(header.fragment, header.fragmentArguments); 198 } 199 break; 200 } 201 } 202 }; 203 204 private static class HeaderAdapter extends ArrayAdapter<Header> { 205 private static class HeaderViewHolder { 206 ImageView icon; 207 TextView title; 208 TextView summary; 209 } 210 211 private LayoutInflater mInflater; 212 213 public HeaderAdapter(Context context, List<Header> objects) { 214 super(context, 0, objects); 215 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 216 } 217 218 @Override 219 public View getView(int position, View convertView, ViewGroup parent) { 220 HeaderViewHolder holder; 221 View view; 222 223 if (convertView == null) { 224 view = mInflater.inflate(com.android.internal.R.layout.preference_list_item, 225 parent, false); 226 holder = new HeaderViewHolder(); 227 holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon); 228 holder.title = (TextView) view.findViewById(com.android.internal.R.id.title); 229 holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary); 230 view.setTag(holder); 231 } else { 232 view = convertView; 233 holder = (HeaderViewHolder) view.getTag(); 234 } 235 236 // All view fields must be updated every time, because the view may be recycled 237 Header header = getItem(position); 238 if (header.icon == null) { 239 holder.icon.setImageDrawable(null); 240 holder.icon.setImageResource(header.iconRes); 241 } else { 242 holder.icon.setImageResource(0); 243 holder.icon.setImageDrawable(header.icon); 244 } 245 holder.title.setText(header.title); 246 if (TextUtils.isEmpty(header.summary)) { 247 holder.summary.setVisibility(View.GONE); 248 } else { 249 holder.summary.setVisibility(View.VISIBLE); 250 holder.summary.setText(header.summary); 251 } 252 253 return view; 254 } 255 } 256 257 /** 258 * Description of a single Header item that the user can select. 259 */ 260 public static class Header { 261 /** 262 * Title of the header that is shown to the user. 263 * @attr ref android.R.styleable#PreferenceHeader_title 264 */ 265 public CharSequence title; 266 267 /** 268 * Optional summary describing what this header controls. 269 * @attr ref android.R.styleable#PreferenceHeader_summary 270 */ 271 public CharSequence summary; 272 273 /** 274 * Optional icon resource to show for this header. 275 * @attr ref android.R.styleable#PreferenceHeader_icon 276 */ 277 public int iconRes; 278 279 /** 280 * Optional icon drawable to show for this header. (If this is non-null, 281 * the iconRes will be ignored.) 282 */ 283 public Drawable icon; 284 285 /** 286 * Full class name of the fragment to display when this header is 287 * selected. 288 * @attr ref android.R.styleable#PreferenceHeader_fragment 289 */ 290 public String fragment; 291 292 /** 293 * Optional arguments to supply to the fragment when it is 294 * instantiated. 295 */ 296 public Bundle fragmentArguments; 297 298 /** 299 * Intent to launch when the preference is selected. 300 */ 301 public Intent intent; 302 } 303 304 @Override 305 protected void onCreate(Bundle savedInstanceState) { 306 super.onCreate(savedInstanceState); 307 308 setContentView(com.android.internal.R.layout.preference_list_content); 309 310 mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer); 311 mPrefsContainer = findViewById(com.android.internal.R.id.prefs); 312 boolean hidingHeaders = onIsHidingHeaders(); 313 mSinglePane = hidingHeaders || !onIsMultiPane(); 314 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT); 315 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); 316 317 if (initialFragment != null && mSinglePane) { 318 // If we are just showing a fragment, we want to run in 319 // new fragment mode, but don't need to compute and show 320 // the headers. 321 getListView().setVisibility(View.GONE); 322 mPrefsContainer.setVisibility(View.VISIBLE); 323 switchToHeader(initialFragment, initialArguments); 324 325 } else { 326 // We need to try to build the headers. 327 onBuildHeaders(mHeaders); 328 329 // If there are headers, then at this point we need to show 330 // them and, depending on the screen, we may also show in-line 331 // the currently selected preference fragment. 332 if (mHeaders.size() > 0) { 333 mAdapter = new HeaderAdapter(this, mHeaders); 334 setListAdapter(mAdapter); 335 if (!mSinglePane) { 336 mPrefsContainer.setVisibility(View.VISIBLE); 337 if (initialFragment == null) { 338 Header h = onGetInitialHeader(); 339 initialFragment = h.fragment; 340 initialArguments = h.fragmentArguments; 341 } 342 switchToHeader(initialFragment, initialArguments); 343 } 344 345 // If there are no headers, we are in the old "just show a screen 346 // of preferences" mode. 347 } else { 348 mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE); 349 mPreferenceManager.setOnPreferenceTreeClickListener(this); 350 } 351 } 352 353 getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); 354 355 // see if we should show Back/Next buttons 356 Intent intent = getIntent(); 357 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) { 358 359 findViewById(com.android.internal.R.id.button_bar).setVisibility(View.VISIBLE); 360 361 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button); 362 backButton.setOnClickListener(new OnClickListener() { 363 public void onClick(View v) { 364 setResult(RESULT_CANCELED); 365 finish(); 366 } 367 }); 368 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button); 369 skipButton.setOnClickListener(new OnClickListener() { 370 public void onClick(View v) { 371 setResult(RESULT_OK); 372 finish(); 373 } 374 }); 375 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button); 376 mNextButton.setOnClickListener(new OnClickListener() { 377 public void onClick(View v) { 378 setResult(RESULT_OK); 379 finish(); 380 } 381 }); 382 383 // set our various button parameters 384 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) { 385 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT); 386 if (TextUtils.isEmpty(buttonText)) { 387 mNextButton.setVisibility(View.GONE); 388 } 389 else { 390 mNextButton.setText(buttonText); 391 } 392 } 393 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) { 394 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT); 395 if (TextUtils.isEmpty(buttonText)) { 396 backButton.setVisibility(View.GONE); 397 } 398 else { 399 backButton.setText(buttonText); 400 } 401 } 402 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) { 403 skipButton.setVisibility(View.VISIBLE); 404 } 405 } 406 } 407 408 /** 409 * Returns true if this activity is currently showing the header list. 410 */ 411 public boolean hasHeaders() { 412 return getListView().getVisibility() == View.VISIBLE 413 && mPreferenceManager == null; 414 } 415 416 /** 417 * Returns true if this activity is showing multiple panes -- the headers 418 * and a preference fragment. 419 */ 420 public boolean isMultiPane() { 421 return hasHeaders() && mPrefsContainer.getVisibility() == View.VISIBLE; 422 } 423 424 /** 425 * Called to determine if the activity should run in multi-pane mode. 426 * The default implementation returns true if the screen is large 427 * enough. 428 */ 429 public boolean onIsMultiPane() { 430 Configuration config = getResources().getConfiguration(); 431 if ((config.screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK) 432 == Configuration.SCREENLAYOUT_SIZE_XLARGE 433 && config.orientation == Configuration.ORIENTATION_LANDSCAPE) { 434 return true; 435 } 436 return false; 437 } 438 439 /** 440 * Called to determine whether the header list should be hidden. 441 * The default implementation returns the 442 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied. 443 * This is set to false, for example, when the activity is being re-launched 444 * to show a particular preference activity. 445 */ 446 public boolean onIsHidingHeaders() { 447 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false); 448 } 449 450 /** 451 * Called to determine the initial header to be shown. The default 452 * implementation simply returns the fragment of the first header. Note 453 * that the returned Header object does not actually need to exist in 454 * your header list -- whatever its fragment is will simply be used to 455 * show for the initial UI. 456 */ 457 public Header onGetInitialHeader() { 458 return mHeaders.get(0); 459 } 460 461 /** 462 * Called after the header list has been updated ({@link #onBuildHeaders} 463 * has been called and returned due to {@link #invalidateHeaders()}) to 464 * specify the header that should now be selected. The default implementation 465 * returns null to keep whatever header is currently selected. 466 */ 467 public Header onGetNewHeader() { 468 return null; 469 } 470 471 /** 472 * Called when the activity needs its list of headers build. By 473 * implementing this and adding at least one item to the list, you 474 * will cause the activity to run in its modern fragment mode. Note 475 * that this function may not always be called; for example, if the 476 * activity has been asked to display a particular fragment without 477 * the header list, there is no need to build the headers. 478 * 479 * <p>Typical implementations will use {@link #loadHeadersFromResource} 480 * to fill in the list from a resource. 481 * 482 * @param target The list in which to place the headers. 483 */ 484 public void onBuildHeaders(List<Header> target) { 485 } 486 487 /** 488 * Call when you need to change the headers being displayed. Will result 489 * in onBuildHeaders() later being called to retrieve the new list. 490 */ 491 public void invalidateHeaders() { 492 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) { 493 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS); 494 } 495 } 496 497 /** 498 * Parse the given XML file as a header description, adding each 499 * parsed Header into the target list. 500 * 501 * @param resid The XML resource to load and parse. 502 * @param target The list in which the parsed headers should be placed. 503 */ 504 public void loadHeadersFromResource(int resid, List<Header> target) { 505 XmlResourceParser parser = null; 506 try { 507 parser = getResources().getXml(resid); 508 AttributeSet attrs = Xml.asAttributeSet(parser); 509 510 int type; 511 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 512 && type != XmlPullParser.START_TAG) { 513 } 514 515 String nodeName = parser.getName(); 516 if (!"preference-headers".equals(nodeName)) { 517 throw new RuntimeException( 518 "XML document must start with <preference-headers> tag; found" 519 + nodeName + " at " + parser.getPositionDescription()); 520 } 521 522 Bundle curBundle = null; 523 524 final int outerDepth = parser.getDepth(); 525 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 526 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 527 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 528 continue; 529 } 530 531 nodeName = parser.getName(); 532 if ("header".equals(nodeName)) { 533 Header header = new Header(); 534 535 TypedArray sa = getResources().obtainAttributes(attrs, 536 com.android.internal.R.styleable.PreferenceHeader); 537 header.title = sa.getText( 538 com.android.internal.R.styleable.PreferenceHeader_title); 539 header.summary = sa.getText( 540 com.android.internal.R.styleable.PreferenceHeader_summary); 541 header.iconRes = sa.getResourceId( 542 com.android.internal.R.styleable.PreferenceHeader_icon, 0); 543 header.fragment = sa.getString( 544 com.android.internal.R.styleable.PreferenceHeader_fragment); 545 sa.recycle(); 546 547 if (curBundle == null) { 548 curBundle = new Bundle(); 549 } 550 551 final int innerDepth = parser.getDepth(); 552 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 553 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { 554 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 555 continue; 556 } 557 558 String innerNodeName = parser.getName(); 559 if (innerNodeName.equals("extra")) { 560 getResources().parseBundleExtra("extra", attrs, curBundle); 561 XmlUtils.skipCurrentTag(parser); 562 563 } else if (innerNodeName.equals("intent")) { 564 header.intent = Intent.parseIntent(getResources(), parser, attrs); 565 566 } else { 567 XmlUtils.skipCurrentTag(parser); 568 } 569 } 570 571 if (curBundle.size() > 0) { 572 header.fragmentArguments = curBundle; 573 curBundle = null; 574 } 575 576 target.add(header); 577 } else { 578 XmlUtils.skipCurrentTag(parser); 579 } 580 } 581 582 } catch (XmlPullParserException e) { 583 throw new RuntimeException("Error parsing headers", e); 584 } catch (IOException e) { 585 throw new RuntimeException("Error parsing headers", e); 586 } finally { 587 if (parser != null) parser.close(); 588 } 589 590 } 591 592 /** 593 * Set a footer that should be shown at the bottom of the header list. 594 */ 595 public void setListFooter(View view) { 596 mListFooter.removeAllViews(); 597 mListFooter.addView(view, new FrameLayout.LayoutParams( 598 FrameLayout.LayoutParams.MATCH_PARENT, 599 FrameLayout.LayoutParams.WRAP_CONTENT)); 600 } 601 602 @Override 603 protected void onStop() { 604 super.onStop(); 605 606 if (mPreferenceManager != null) { 607 mPreferenceManager.dispatchActivityStop(); 608 } 609 } 610 611 @Override 612 protected void onDestroy() { 613 super.onDestroy(); 614 615 if (mPreferenceManager != null) { 616 mPreferenceManager.dispatchActivityDestroy(); 617 } 618 } 619 620 @Override 621 protected void onSaveInstanceState(Bundle outState) { 622 super.onSaveInstanceState(outState); 623 624 if (mPreferenceManager != null) { 625 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 626 if (preferenceScreen != null) { 627 Bundle container = new Bundle(); 628 preferenceScreen.saveHierarchyState(container); 629 outState.putBundle(PREFERENCES_TAG, container); 630 } 631 } 632 } 633 634 @Override 635 protected void onRestoreInstanceState(Bundle state) { 636 if (mPreferenceManager != null) { 637 Bundle container = state.getBundle(PREFERENCES_TAG); 638 if (container != null) { 639 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 640 if (preferenceScreen != null) { 641 preferenceScreen.restoreHierarchyState(container); 642 mSavedInstanceState = state; 643 return; 644 } 645 } 646 } 647 648 // Only call this if we didn't save the instance state for later. 649 // If we did save it, it will be restored when we bind the adapter. 650 super.onRestoreInstanceState(state); 651 } 652 653 @Override 654 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 655 super.onActivityResult(requestCode, resultCode, data); 656 657 if (mPreferenceManager != null) { 658 mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data); 659 } 660 } 661 662 @Override 663 public void onContentChanged() { 664 super.onContentChanged(); 665 666 if (mPreferenceManager != null) { 667 postBindPreferences(); 668 } 669 } 670 671 @Override 672 protected void onListItemClick(ListView l, View v, int position, long id) { 673 super.onListItemClick(l, v, position, id); 674 675 if (mAdapter != null) { 676 onHeaderClick(mHeaders.get(position), position); 677 } 678 } 679 680 /** 681 * Called when the user selects an item in the header list. The default 682 * implementation will call either {@link #startWithFragment(String, Bundle)} 683 * or {@link #switchToHeader(String, Bundle)} as appropriate. 684 * 685 * @param header The header that was selected. 686 * @param position The header's position in the list. 687 */ 688 public void onHeaderClick(Header header, int position) { 689 if (header.fragment != null) { 690 if (mSinglePane) { 691 startWithFragment(header.fragment, header.fragmentArguments); 692 } else { 693 switchToHeader(header.fragment, header.fragmentArguments); 694 } 695 } else if (header.intent != null) { 696 startActivity(header.intent); 697 } 698 } 699 700 /** 701 * Start a new instance of this activity, showing only the given 702 * preference fragment. When launched in this mode, the header list 703 * will be hidden and the given preference fragment will be instantiated 704 * and fill the entire activity. 705 * 706 * @param fragmentName The name of the fragment to display. 707 * @param args Optional arguments to supply to the fragment. 708 */ 709 public void startWithFragment(String fragmentName, Bundle args) { 710 Intent intent = new Intent(Intent.ACTION_MAIN); 711 intent.setClass(this, getClass()); 712 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName); 713 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); 714 intent.putExtra(EXTRA_NO_HEADERS, true); 715 startActivity(intent); 716 } 717 718 /** 719 * When in two-pane mode, switch the fragment pane to show the given 720 * preference fragment. 721 * 722 * @param fragmentName The name of the fragment to display. 723 * @param args Optional arguments to supply to the fragment. 724 */ 725 public void switchToHeader(String fragmentName, Bundle args) { 726 getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE); 727 728 Fragment f = Fragment.instantiate(this, fragmentName, args); 729 getFragmentManager().openTransaction().replace( 730 com.android.internal.R.id.prefs, f).commit(); 731 } 732 733 @Override 734 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { 735 Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras()); 736 getFragmentManager().openTransaction().replace(com.android.internal.R.id.prefs, f) 737 .addToBackStack(BACK_STACK_PREFS).commit(); 738 return true; 739 } 740 741 /** 742 * Posts a message to bind the preferences to the list view. 743 * <p> 744 * Binding late is preferred as any custom preference types created in 745 * {@link #onCreate(Bundle)} are able to have their views recycled. 746 */ 747 private void postBindPreferences() { 748 if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return; 749 mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); 750 } 751 752 private void bindPreferences() { 753 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 754 if (preferenceScreen != null) { 755 preferenceScreen.bind(getListView()); 756 if (mSavedInstanceState != null) { 757 super.onRestoreInstanceState(mSavedInstanceState); 758 mSavedInstanceState = null; 759 } 760 } 761 } 762 763 /** 764 * Returns the {@link PreferenceManager} used by this activity. 765 * @return The {@link PreferenceManager}. 766 * 767 * @deprecated This function is not relevant for a modern fragment-based 768 * PreferenceActivity. 769 */ 770 @Deprecated 771 public PreferenceManager getPreferenceManager() { 772 return mPreferenceManager; 773 } 774 775 private void requirePreferenceManager() { 776 if (mPreferenceManager == null) { 777 if (mAdapter == null) { 778 throw new RuntimeException("This should be called after super.onCreate."); 779 } 780 throw new RuntimeException( 781 "Modern two-pane PreferenceActivity requires use of a PreferenceFragment"); 782 } 783 } 784 785 /** 786 * Sets the root of the preference hierarchy that this activity is showing. 787 * 788 * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. 789 * 790 * @deprecated This function is not relevant for a modern fragment-based 791 * PreferenceActivity. 792 */ 793 @Deprecated 794 public void setPreferenceScreen(PreferenceScreen preferenceScreen) { 795 requirePreferenceManager(); 796 797 if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) { 798 postBindPreferences(); 799 CharSequence title = getPreferenceScreen().getTitle(); 800 // Set the title of the activity 801 if (title != null) { 802 setTitle(title); 803 } 804 } 805 } 806 807 /** 808 * Gets the root of the preference hierarchy that this activity is showing. 809 * 810 * @return The {@link PreferenceScreen} that is the root of the preference 811 * hierarchy. 812 * 813 * @deprecated This function is not relevant for a modern fragment-based 814 * PreferenceActivity. 815 */ 816 @Deprecated 817 public PreferenceScreen getPreferenceScreen() { 818 if (mPreferenceManager != null) { 819 return mPreferenceManager.getPreferenceScreen(); 820 } 821 return null; 822 } 823 824 /** 825 * Adds preferences from activities that match the given {@link Intent}. 826 * 827 * @param intent The {@link Intent} to query activities. 828 * 829 * @deprecated This function is not relevant for a modern fragment-based 830 * PreferenceActivity. 831 */ 832 @Deprecated 833 public void addPreferencesFromIntent(Intent intent) { 834 requirePreferenceManager(); 835 836 setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen())); 837 } 838 839 /** 840 * Inflates the given XML resource and adds the preference hierarchy to the current 841 * preference hierarchy. 842 * 843 * @param preferencesResId The XML resource ID to inflate. 844 * 845 * @deprecated This function is not relevant for a modern fragment-based 846 * PreferenceActivity. 847 */ 848 @Deprecated 849 public void addPreferencesFromResource(int preferencesResId) { 850 requirePreferenceManager(); 851 852 setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId, 853 getPreferenceScreen())); 854 } 855 856 /** 857 * {@inheritDoc} 858 * 859 * @deprecated This function is not relevant for a modern fragment-based 860 * PreferenceActivity. 861 */ 862 @Deprecated 863 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 864 return false; 865 } 866 867 /** 868 * Finds a {@link Preference} based on its key. 869 * 870 * @param key The key of the preference to retrieve. 871 * @return The {@link Preference} with the key, or null. 872 * @see PreferenceGroup#findPreference(CharSequence) 873 * 874 * @deprecated This function is not relevant for a modern fragment-based 875 * PreferenceActivity. 876 */ 877 @Deprecated 878 public Preference findPreference(CharSequence key) { 879 880 if (mPreferenceManager == null) { 881 return null; 882 } 883 884 return mPreferenceManager.findPreference(key); 885 } 886 887 @Override 888 protected void onNewIntent(Intent intent) { 889 if (mPreferenceManager != null) { 890 mPreferenceManager.dispatchNewIntent(intent); 891 } 892 } 893 894 // give subclasses access to the Next button 895 /** @hide */ 896 protected boolean hasNextButton() { 897 return mNextButton != null; 898 } 899 /** @hide */ 900 protected Button getNextButton() { 901 return mNextButton; 902 } 903} 904