PreferenceActivity.java revision b3cf10ffa8ff9cac0da8b23a0d84076b3f501400
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    private static final String EXTRA_PREFS_SHOW_FRAGMENT = ":android:show_fragment";
117
118    private static final String EXTRA_PREFS_NO_HEADERS = ":android:no_headers";
119
120    private static final String BACK_STACK_PREFS = ":android:prefs";
121
122    // extras that allow any preference activity to be launched as part of a wizard
123
124    // show Back and Next buttons? takes boolean parameter
125    // Back will then return RESULT_CANCELED and Next RESULT_OK
126    private static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
127
128    // specify custom text for the Back or Next buttons, or cause a button to not appear
129    // at all by setting it to null
130    private static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
131    private static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
132
133    // --- State for new mode when showing a list of headers + prefs fragment
134
135    private final ArrayList<Header> mHeaders = new ArrayList<Header>();
136
137    private HeaderAdapter mAdapter;
138
139    private View mPrefsContainer;
140
141    private boolean mSinglePane;
142
143    // --- State for old mode when showing a single preference list
144
145    private PreferenceManager mPreferenceManager;
146
147    private Bundle mSavedInstanceState;
148
149    // --- Common state
150
151    private Button mNextButton;
152
153    /**
154     * The starting request code given out to preference framework.
155     */
156    private static final int FIRST_REQUEST_CODE = 100;
157
158    private static final int MSG_BIND_PREFERENCES = 0;
159    private Handler mHandler = new Handler() {
160        @Override
161        public void handleMessage(Message msg) {
162            switch (msg.what) {
163
164                case MSG_BIND_PREFERENCES:
165                    bindPreferences();
166                    break;
167            }
168        }
169    };
170
171    private class HeaderViewHolder {
172        ImageView icon;
173        TextView title;
174        TextView summary;
175    }
176
177    private class HeaderAdapter extends ArrayAdapter<Header> {
178        private LayoutInflater mInflater;
179
180        public HeaderAdapter(Context context, List<Header> objects) {
181            super(context, 0, objects);
182            mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
183        }
184
185        @Override
186        public View getView(int position, View convertView, ViewGroup parent) {
187            HeaderViewHolder holder;
188            View view;
189
190            if (convertView == null) {
191                view = mInflater.inflate(com.android.internal.R.layout.preference_list_item,
192                        parent, false);
193                holder = new HeaderViewHolder();
194                holder.icon = (ImageView)view.findViewById(
195                        com.android.internal.R.id.icon);
196                holder.title = (TextView)view.findViewById(
197                        com.android.internal.R.id.title);
198                holder.summary = (TextView)view.findViewById(
199                        com.android.internal.R.id.summary);
200                view.setTag(holder);
201            } else {
202                view = convertView;
203                holder = (HeaderViewHolder)view.getTag();
204            }
205
206            Header header = getItem(position);
207            if (header.icon != null) holder.icon.setImageDrawable(header.icon);
208            else if (header.iconRes != 0) holder.icon.setImageResource(header.iconRes);
209            if (header.title != null) holder.title.setText(header.title);
210            if (header.summary != null) holder.summary.setText(header.summary);
211
212            return view;
213        }
214    }
215
216    /**
217     * Description of a single Header item that the user can select.
218     */
219    public static class Header {
220        /**
221         * Title of the header that is shown to the user.
222         * @attr ref android.R.styleable#PreferenceHeader_title
223         */
224        CharSequence title;
225
226        /**
227         * Optional summary describing what this header controls.
228         * @attr ref android.R.styleable#PreferenceHeader_summary
229         */
230        CharSequence summary;
231
232        /**
233         * Optional icon resource to show for this header.
234         * @attr ref android.R.styleable#PreferenceHeader_icon
235         */
236        int iconRes;
237
238        /**
239         * Optional icon drawable to show for this header.  (If this is non-null,
240         * the iconRes will be ignored.)
241         */
242        Drawable icon;
243
244        /**
245         * Full class name of the fragment to display when this header is
246         * selected.
247         * @attr ref android.R.styleable#PreferenceHeader_fragment
248         */
249        String fragment;
250    }
251
252    @Override
253    protected void onCreate(Bundle savedInstanceState) {
254        super.onCreate(savedInstanceState);
255
256        setContentView(com.android.internal.R.layout.preference_list_content);
257
258        mPrefsContainer = findViewById(com.android.internal.R.id.prefs);
259        boolean hidingHeaders = onIsHidingHeaders();
260        mSinglePane = hidingHeaders || !onIsMultiPane();
261        String initialFragment = getIntent().getStringExtra(EXTRA_PREFS_SHOW_FRAGMENT);
262
263        if (initialFragment != null && mSinglePane) {
264            // If we are just showing a fragment, we want to run in
265            // new fragment mode, but don't need to compute and show
266            // the headers.
267            getListView().setVisibility(View.GONE);
268            mPrefsContainer.setVisibility(View.VISIBLE);
269            switchToHeader(initialFragment);
270
271        } else {
272            // We need to try to build the headers.
273            onBuildHeaders(mHeaders);
274
275            // If there are headers, then at this point we need to show
276            // them and, depending on the screen, we may also show in-line
277            // the currently selected preference fragment.
278            if (mHeaders.size() > 0) {
279                mAdapter = new HeaderAdapter(this, mHeaders);
280                setListAdapter(mAdapter);
281                if (!mSinglePane) {
282                    mPrefsContainer.setVisibility(View.VISIBLE);
283                    switchToHeader(initialFragment != null
284                            ? initialFragment : onGetInitialFragment());
285                }
286
287            // If there are no headers, we are in the old "just show a screen
288            // of preferences" mode.
289            } else {
290                mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
291                mPreferenceManager.setOnPreferenceTreeClickListener(this);
292            }
293        }
294
295        getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
296
297        // see if we should show Back/Next buttons
298        Intent intent = getIntent();
299        if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
300
301            findViewById(com.android.internal.R.id.button_bar).setVisibility(View.VISIBLE);
302
303            Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
304            backButton.setOnClickListener(new OnClickListener() {
305                public void onClick(View v) {
306                    setResult(RESULT_CANCELED);
307                    finish();
308                }
309            });
310            mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
311            mNextButton.setOnClickListener(new OnClickListener() {
312                public void onClick(View v) {
313                    setResult(RESULT_OK);
314                    finish();
315                }
316            });
317
318            // set our various button parameters
319            if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
320                String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
321                if (TextUtils.isEmpty(buttonText)) {
322                    mNextButton.setVisibility(View.GONE);
323                }
324                else {
325                    mNextButton.setText(buttonText);
326                }
327            }
328            if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
329                String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
330                if (TextUtils.isEmpty(buttonText)) {
331                    backButton.setVisibility(View.GONE);
332                }
333                else {
334                    backButton.setText(buttonText);
335                }
336            }
337        }
338    }
339
340    /**
341     * Called to determine if the activity should run in multi-pane mode.
342     * The default implementation returns true if the screen is large
343     * enough.
344     */
345    public boolean onIsMultiPane() {
346        Configuration config = getResources().getConfiguration();
347        if ((config.screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
348                == Configuration.SCREENLAYOUT_SIZE_XLARGE
349                && config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
350            return true;
351        }
352        return false;
353    }
354
355    /**
356     * Called to determine whether the header list should be hidden.  The
357     * default implementation hides the list if the activity is being re-launched
358     * when not in multi-pane mode.
359     */
360    public boolean onIsHidingHeaders() {
361        return getIntent().getBooleanExtra(EXTRA_PREFS_NO_HEADERS, false);
362    }
363
364    /**
365     * Called to determine the initial fragment to be shown.  The default
366     * implementation simply returns the fragment of the first header.
367     */
368    public String onGetInitialFragment() {
369        return mHeaders.get(0).fragment;
370    }
371
372    /**
373     * Called when the activity needs its list of headers build.  By
374     * implementing this and adding at least one item to the list, you
375     * will cause the activity to run in its modern fragment mode.  Note
376     * that this function may not always be called; for example, if the
377     * activity has been asked to display a particular fragment without
378     * the header list, there is no need to build the headers.
379     *
380     * <p>Typical implementations will use {@link #loadHeadersFromResource}
381     * to fill in the list from a resource.
382     *
383     * @param target The list in which to place the headers.
384     */
385    public void onBuildHeaders(List<Header> target) {
386    }
387
388    /**
389     * Parse the given XML file as a header description, adding each
390     * parsed Header into the target list.
391     *
392     * @param resid The XML resource to load and parse.
393     * @param target The list in which the parsed headers should be placed.
394     */
395    public void loadHeadersFromResource(int resid, List<Header> target) {
396        XmlResourceParser parser = null;
397        try {
398            parser = getResources().getXml(resid);
399            AttributeSet attrs = Xml.asAttributeSet(parser);
400
401            int type;
402            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
403                    && type != XmlPullParser.START_TAG) {
404            }
405
406            String nodeName = parser.getName();
407            if (!"PreferenceHeaders".equals(nodeName)) {
408                throw new RuntimeException(
409                        "XML document must start with <PreferenceHeaders> tag; found"
410                        + nodeName + " at " + parser.getPositionDescription());
411            }
412
413            int outerDepth = parser.getDepth();
414            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
415                   && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
416                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
417                    continue;
418                }
419
420                nodeName = parser.getName();
421                if ("Header".equals(nodeName)) {
422                    Header header = new Header();
423
424                    TypedArray sa = getResources().obtainAttributes(attrs,
425                            com.android.internal.R.styleable.PreferenceHeader);
426                    header.title = sa.getText(
427                            com.android.internal.R.styleable.PreferenceHeader_title);
428                    header.summary = sa.getText(
429                            com.android.internal.R.styleable.PreferenceHeader_summary);
430                    header.iconRes = sa.getResourceId(
431                            com.android.internal.R.styleable.PreferenceHeader_icon, 0);
432                    header.fragment = sa.getString(
433                            com.android.internal.R.styleable.PreferenceHeader_fragment);
434                    sa.recycle();
435
436                    target.add(header);
437
438                    XmlUtils.skipCurrentTag(parser);
439                } else {
440                    XmlUtils.skipCurrentTag(parser);
441                }
442            }
443
444        } catch (XmlPullParserException e) {
445            throw new RuntimeException("Error parsing headers", e);
446        } catch (IOException e) {
447            throw new RuntimeException("Error parsing headers", e);
448        } finally {
449            if (parser != null) parser.close();
450        }
451
452    }
453
454    @Override
455    protected void onStop() {
456        super.onStop();
457
458        if (mPreferenceManager != null) {
459            mPreferenceManager.dispatchActivityStop();
460        }
461    }
462
463    @Override
464    protected void onDestroy() {
465        super.onDestroy();
466
467        if (mPreferenceManager != null) {
468            mPreferenceManager.dispatchActivityDestroy();
469        }
470    }
471
472    @Override
473    protected void onSaveInstanceState(Bundle outState) {
474        super.onSaveInstanceState(outState);
475
476        if (mPreferenceManager != null) {
477            final PreferenceScreen preferenceScreen = getPreferenceScreen();
478            if (preferenceScreen != null) {
479                Bundle container = new Bundle();
480                preferenceScreen.saveHierarchyState(container);
481                outState.putBundle(PREFERENCES_TAG, container);
482            }
483        }
484    }
485
486    @Override
487    protected void onRestoreInstanceState(Bundle state) {
488        if (mPreferenceManager != null) {
489            Bundle container = state.getBundle(PREFERENCES_TAG);
490            if (container != null) {
491                final PreferenceScreen preferenceScreen = getPreferenceScreen();
492                if (preferenceScreen != null) {
493                    preferenceScreen.restoreHierarchyState(container);
494                    mSavedInstanceState = state;
495                    return;
496                }
497            }
498        }
499
500        // Only call this if we didn't save the instance state for later.
501        // If we did save it, it will be restored when we bind the adapter.
502        super.onRestoreInstanceState(state);
503    }
504
505    @Override
506    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
507        super.onActivityResult(requestCode, resultCode, data);
508
509        if (mPreferenceManager != null) {
510            mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
511        }
512    }
513
514    @Override
515    public void onContentChanged() {
516        super.onContentChanged();
517
518        if (mPreferenceManager != null) {
519            postBindPreferences();
520        }
521    }
522
523    @Override
524    protected void onListItemClick(ListView l, View v, int position, long id) {
525        super.onListItemClick(l, v, position, id);
526
527        if (mAdapter != null) {
528            onHeaderClick(mHeaders.get(position), position);
529        }
530    }
531
532    /**
533     * Called when the user selects an item in the header list.  The default
534     * implementation will call either {@link #startWithFragment(String)}
535     * or {@link #switchToHeader(String)} as appropriate.
536     *
537     * @param header The header that was selected.
538     * @param position The header's position in the list.
539     */
540    public void onHeaderClick(Header header, int position) {
541        if (mSinglePane) {
542            startWithFragment(header.fragment);
543        } else {
544            switchToHeader(header.fragment);
545        }
546    }
547
548    /**
549     * Start a new instance of this activity, showing only the given
550     * preference fragment.  When launched in this mode, the header list
551     * will be hidden and the given preference fragment will be instantiated
552     * and fill the entire activity.
553     *
554     * @param fragmentName The name of the fragment to display.
555     */
556    public void startWithFragment(String fragmentName) {
557        Intent intent = new Intent(Intent.ACTION_MAIN);
558        intent.setClass(this, getClass());
559        intent.putExtra(EXTRA_PREFS_SHOW_FRAGMENT, fragmentName);
560        intent.putExtra(EXTRA_PREFS_NO_HEADERS, true);
561        startActivity(intent);
562    }
563
564    /**
565     * When in two-pane mode, switch the fragment pane to show the given
566     * preference fragment.
567     *
568     * @param fragmentName The name of the fragment to display.
569     */
570    public void switchToHeader(String fragmentName) {
571        popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
572
573        Fragment f;
574        try {
575            f = Fragment.instantiate(this, fragmentName);
576        } catch (Exception e) {
577            Log.w(TAG, "Failure instantiating fragment " + fragmentName, e);
578            return;
579        }
580        openFragmentTransaction().replace(com.android.internal.R.id.prefs, f).commit();
581    }
582
583    @Override
584    public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
585        Fragment f;
586        try {
587            f = Fragment.instantiate(this, pref.getFragment());
588        } catch (Exception e) {
589            Log.w(TAG, "Failure instantiating fragment " + pref.getFragment(), e);
590            return false;
591        }
592        openFragmentTransaction().replace(com.android.internal.R.id.prefs, f)
593                .addToBackStack(BACK_STACK_PREFS).commit();
594        return true;
595    }
596
597    /**
598     * Posts a message to bind the preferences to the list view.
599     * <p>
600     * Binding late is preferred as any custom preference types created in
601     * {@link #onCreate(Bundle)} are able to have their views recycled.
602     */
603    private void postBindPreferences() {
604        if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
605        mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
606    }
607
608    private void bindPreferences() {
609        final PreferenceScreen preferenceScreen = getPreferenceScreen();
610        if (preferenceScreen != null) {
611            preferenceScreen.bind(getListView());
612            if (mSavedInstanceState != null) {
613                super.onRestoreInstanceState(mSavedInstanceState);
614                mSavedInstanceState = null;
615            }
616        }
617    }
618
619    /**
620     * Returns the {@link PreferenceManager} used by this activity.
621     * @return The {@link PreferenceManager}.
622     *
623     * @deprecated This function is not relevant for a modern fragment-based
624     * PreferenceActivity.
625     */
626    @Deprecated
627    public PreferenceManager getPreferenceManager() {
628        return mPreferenceManager;
629    }
630
631    private void requirePreferenceManager() {
632        if (mPreferenceManager == null) {
633            if (mAdapter == null) {
634                throw new RuntimeException("This should be called after super.onCreate.");
635            }
636            throw new RuntimeException(
637                    "Modern two-pane PreferenceActivity requires use of a PreferenceFragment");
638        }
639    }
640
641    /**
642     * Sets the root of the preference hierarchy that this activity is showing.
643     *
644     * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
645     *
646     * @deprecated This function is not relevant for a modern fragment-based
647     * PreferenceActivity.
648     */
649    @Deprecated
650    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
651        requirePreferenceManager();
652
653        if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
654            postBindPreferences();
655            CharSequence title = getPreferenceScreen().getTitle();
656            // Set the title of the activity
657            if (title != null) {
658                setTitle(title);
659            }
660        }
661    }
662
663    /**
664     * Gets the root of the preference hierarchy that this activity is showing.
665     *
666     * @return The {@link PreferenceScreen} that is the root of the preference
667     *         hierarchy.
668     *
669     * @deprecated This function is not relevant for a modern fragment-based
670     * PreferenceActivity.
671     */
672    @Deprecated
673    public PreferenceScreen getPreferenceScreen() {
674        if (mPreferenceManager != null) {
675            return mPreferenceManager.getPreferenceScreen();
676        }
677        return null;
678    }
679
680    /**
681     * Adds preferences from activities that match the given {@link Intent}.
682     *
683     * @param intent The {@link Intent} to query activities.
684     *
685     * @deprecated This function is not relevant for a modern fragment-based
686     * PreferenceActivity.
687     */
688    @Deprecated
689    public void addPreferencesFromIntent(Intent intent) {
690        requirePreferenceManager();
691
692        setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen()));
693    }
694
695    /**
696     * Inflates the given XML resource and adds the preference hierarchy to the current
697     * preference hierarchy.
698     *
699     * @param preferencesResId The XML resource ID to inflate.
700     *
701     * @deprecated This function is not relevant for a modern fragment-based
702     * PreferenceActivity.
703     */
704    @Deprecated
705    public void addPreferencesFromResource(int preferencesResId) {
706        requirePreferenceManager();
707
708        setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId,
709                getPreferenceScreen()));
710    }
711
712    /**
713     * {@inheritDoc}
714     *
715     * @deprecated This function is not relevant for a modern fragment-based
716     * PreferenceActivity.
717     */
718    @Deprecated
719    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
720        return false;
721    }
722
723    /**
724     * Finds a {@link Preference} based on its key.
725     *
726     * @param key The key of the preference to retrieve.
727     * @return The {@link Preference} with the key, or null.
728     * @see PreferenceGroup#findPreference(CharSequence)
729     *
730     * @deprecated This function is not relevant for a modern fragment-based
731     * PreferenceActivity.
732     */
733    @Deprecated
734    public Preference findPreference(CharSequence key) {
735
736        if (mPreferenceManager == null) {
737            return null;
738        }
739
740        return mPreferenceManager.findPreference(key);
741    }
742
743    @Override
744    protected void onNewIntent(Intent intent) {
745        if (mPreferenceManager != null) {
746            mPreferenceManager.dispatchNewIntent(intent);
747        }
748    }
749
750    // give subclasses access to the Next button
751    /** @hide */
752    protected boolean hasNextButton() {
753        return mNextButton != null;
754    }
755    /** @hide */
756    protected Button getNextButton() {
757        return mNextButton;
758    }
759}
760