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