Preference.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.CharSequences;
20
21import android.content.Context;
22import android.content.Intent;
23import android.content.SharedPreferences;
24import android.content.res.TypedArray;
25import android.os.Bundle;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.text.TextUtils;
29import android.util.AttributeSet;
30import android.view.AbsSavedState;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.widget.ListView;
35import android.widget.TextView;
36
37import java.util.ArrayList;
38import java.util.List;
39import java.util.Set;
40
41/**
42 * Represents the basic Preference UI building
43 * block displayed by a {@link PreferenceActivity} in the form of a
44 * {@link ListView}. This class provides the {@link View} to be displayed in
45 * the activity and associates with a {@link SharedPreferences} to
46 * store/retrieve the preference data.
47 * <p>
48 * When specifying a preference hierarchy in XML, each element can point to a
49 * subclass of {@link Preference}, similar to the view hierarchy and layouts.
50 * <p>
51 * This class contains a {@code key} that will be used as the key into the
52 * {@link SharedPreferences}. It is up to the subclass to decide how to store
53 * the value.
54 *
55 * @attr ref android.R.styleable#Preference_key
56 * @attr ref android.R.styleable#Preference_title
57 * @attr ref android.R.styleable#Preference_summary
58 * @attr ref android.R.styleable#Preference_order
59 * @attr ref android.R.styleable#Preference_fragment
60 * @attr ref android.R.styleable#Preference_layout
61 * @attr ref android.R.styleable#Preference_widgetLayout
62 * @attr ref android.R.styleable#Preference_enabled
63 * @attr ref android.R.styleable#Preference_selectable
64 * @attr ref android.R.styleable#Preference_dependency
65 * @attr ref android.R.styleable#Preference_persistent
66 * @attr ref android.R.styleable#Preference_defaultValue
67 * @attr ref android.R.styleable#Preference_shouldDisableView
68 */
69public class Preference implements Comparable<Preference>, OnDependencyChangeListener {
70    /**
71     * Specify for {@link #setOrder(int)} if a specific order is not required.
72     */
73    public static final int DEFAULT_ORDER = Integer.MAX_VALUE;
74
75    private Context mContext;
76    private PreferenceManager mPreferenceManager;
77
78    /**
79     * Set when added to hierarchy since we need a unique ID within that
80     * hierarchy.
81     */
82    private long mId;
83
84    private OnPreferenceChangeListener mOnChangeListener;
85    private OnPreferenceClickListener mOnClickListener;
86
87    private int mOrder = DEFAULT_ORDER;
88    private CharSequence mTitle;
89    private CharSequence mSummary;
90    private String mKey;
91    private Intent mIntent;
92    private String mFragment;
93    private boolean mEnabled = true;
94    private boolean mSelectable = true;
95    private boolean mRequiresKey;
96    private boolean mPersistent = true;
97    private String mDependencyKey;
98    private Object mDefaultValue;
99    private boolean mDependencyMet = true;
100
101    /**
102     * @see #setShouldDisableView(boolean)
103     */
104    private boolean mShouldDisableView = true;
105
106    private int mLayoutResId = com.android.internal.R.layout.preference;
107    private int mWidgetLayoutResId;
108    private boolean mHasSpecifiedLayout = false;
109
110    private OnPreferenceChangeInternalListener mListener;
111
112    private List<Preference> mDependents;
113
114    private boolean mBaseMethodCalled;
115
116    /**
117     * Interface definition for a callback to be invoked when the value of this
118     * {@link Preference} has been changed by the user and is
119     * about to be set and/or persisted.  This gives the client a chance
120     * to prevent setting and/or persisting the value.
121     */
122    public interface OnPreferenceChangeListener {
123        /**
124         * Called when a Preference has been changed by the user. This is
125         * called before the state of the Preference is about to be updated and
126         * before the state is persisted.
127         *
128         * @param preference The changed Preference.
129         * @param newValue The new value of the Preference.
130         * @return True to update the state of the Preference with the new value.
131         */
132        boolean onPreferenceChange(Preference preference, Object newValue);
133    }
134
135    /**
136     * Interface definition for a callback to be invoked when a {@link Preference} is
137     * clicked.
138     */
139    public interface OnPreferenceClickListener {
140        /**
141         * Called when a Preference has been clicked.
142         *
143         * @param preference The Preference that was clicked.
144         * @return True if the click was handled.
145         */
146        boolean onPreferenceClick(Preference preference);
147    }
148
149    /**
150     * Interface definition for a callback to be invoked when this
151     * {@link Preference} is changed or, if this is a group, there is an
152     * addition/removal of {@link Preference}(s). This is used internally.
153     */
154    interface OnPreferenceChangeInternalListener {
155        /**
156         * Called when this Preference has changed.
157         *
158         * @param preference This preference.
159         */
160        void onPreferenceChange(Preference preference);
161
162        /**
163         * Called when this group has added/removed {@link Preference}(s).
164         *
165         * @param preference This Preference.
166         */
167        void onPreferenceHierarchyChange(Preference preference);
168    }
169
170    /**
171     * Perform inflation from XML and apply a class-specific base style. This
172     * constructor of Preference allows subclasses to use their own base
173     * style when they are inflating. For example, a {@link CheckBoxPreference}
174     * constructor calls this version of the super class constructor and
175     * supplies {@code android.R.attr.checkBoxPreferenceStyle} for <var>defStyle</var>.
176     * This allows the theme's checkbox preference style to modify all of the base
177     * preference attributes as well as the {@link CheckBoxPreference} class's
178     * attributes.
179     *
180     * @param context The Context this is associated with, through which it can
181     *            access the current theme, resources, {@link SharedPreferences},
182     *            etc.
183     * @param attrs The attributes of the XML tag that is inflating the preference.
184     * @param defStyle The default style to apply to this preference. If 0, no style
185     *            will be applied (beyond what is included in the theme). This
186     *            may either be an attribute resource, whose value will be
187     *            retrieved from the current theme, or an explicit style
188     *            resource.
189     * @see #Preference(Context, AttributeSet)
190     */
191    public Preference(Context context, AttributeSet attrs, int defStyle) {
192        mContext = context;
193
194        TypedArray a = context.obtainStyledAttributes(attrs,
195                com.android.internal.R.styleable.Preference, defStyle, 0);
196        for (int i = a.getIndexCount(); i >= 0; i--) {
197            int attr = a.getIndex(i);
198            switch (attr) {
199                case com.android.internal.R.styleable.Preference_key:
200                    mKey = a.getString(attr);
201                    break;
202
203                case com.android.internal.R.styleable.Preference_title:
204                    mTitle = a.getString(attr);
205                    break;
206
207                case com.android.internal.R.styleable.Preference_summary:
208                    mSummary = a.getString(attr);
209                    break;
210
211                case com.android.internal.R.styleable.Preference_order:
212                    mOrder = a.getInt(attr, mOrder);
213                    break;
214
215                case com.android.internal.R.styleable.Preference_fragment:
216                    mFragment = a.getString(attr);
217                    break;
218
219                case com.android.internal.R.styleable.Preference_layout:
220                    mLayoutResId = a.getResourceId(attr, mLayoutResId);
221                    break;
222
223                case com.android.internal.R.styleable.Preference_widgetLayout:
224                    mWidgetLayoutResId = a.getResourceId(attr, mWidgetLayoutResId);
225                    break;
226
227                case com.android.internal.R.styleable.Preference_enabled:
228                    mEnabled = a.getBoolean(attr, true);
229                    break;
230
231                case com.android.internal.R.styleable.Preference_selectable:
232                    mSelectable = a.getBoolean(attr, true);
233                    break;
234
235                case com.android.internal.R.styleable.Preference_persistent:
236                    mPersistent = a.getBoolean(attr, mPersistent);
237                    break;
238
239                case com.android.internal.R.styleable.Preference_dependency:
240                    mDependencyKey = a.getString(attr);
241                    break;
242
243                case com.android.internal.R.styleable.Preference_defaultValue:
244                    mDefaultValue = onGetDefaultValue(a, attr);
245                    break;
246
247                case com.android.internal.R.styleable.Preference_shouldDisableView:
248                    mShouldDisableView = a.getBoolean(attr, mShouldDisableView);
249                    break;
250            }
251        }
252        a.recycle();
253
254        if (!getClass().getName().startsWith("android.preference")) {
255            // For subclasses not in this package, assume the worst and don't cache views
256            mHasSpecifiedLayout = true;
257        }
258    }
259
260    /**
261     * Constructor that is called when inflating a Preference from XML. This is
262     * called when a Preference is being constructed from an XML file, supplying
263     * attributes that were specified in the XML file. This version uses a
264     * default style of 0, so the only attribute values applied are those in the
265     * Context's Theme and the given AttributeSet.
266     *
267     * @param context The Context this is associated with, through which it can
268     *            access the current theme, resources, {@link SharedPreferences},
269     *            etc.
270     * @param attrs The attributes of the XML tag that is inflating the
271     *            preference.
272     * @see #Preference(Context, AttributeSet, int)
273     */
274    public Preference(Context context, AttributeSet attrs) {
275        this(context, attrs, 0);
276    }
277
278    /**
279     * Constructor to create a Preference.
280     *
281     * @param context The Context in which to store Preference values.
282     */
283    public Preference(Context context) {
284        this(context, null);
285    }
286
287    /**
288     * Called when a Preference is being inflated and the default value
289     * attribute needs to be read. Since different Preference types have
290     * different value types, the subclass should get and return the default
291     * value which will be its value type.
292     * <p>
293     * For example, if the value type is String, the body of the method would
294     * proxy to {@link TypedArray#getString(int)}.
295     *
296     * @param a The set of attributes.
297     * @param index The index of the default value attribute.
298     * @return The default value of this preference type.
299     */
300    protected Object onGetDefaultValue(TypedArray a, int index) {
301        return null;
302    }
303
304    /**
305     * Sets an {@link Intent} to be used for
306     * {@link Context#startActivity(Intent)} when this Preference is clicked.
307     *
308     * @param intent The intent associated with this Preference.
309     */
310    public void setIntent(Intent intent) {
311        mIntent = intent;
312    }
313
314    /**
315     * Return the {@link Intent} associated with this Preference.
316     *
317     * @return The {@link Intent} last set via {@link #setIntent(Intent)} or XML.
318     */
319    public Intent getIntent() {
320        return mIntent;
321    }
322
323    /**
324     * Sets the class name of a fragment to be shown when this Preference is clicked.
325     *
326     * @param fragment The class name of the fragment associated with this Preference.
327     */
328    public void setFragment(String fragment) {
329        mFragment = fragment;
330    }
331
332    /**
333     * Return the fragment class name associated with this Preference.
334     *
335     * @return The fragment class name last set via {@link #setFragment} or XML.
336     */
337    public String getFragment() {
338        return mFragment;
339    }
340
341    /**
342     * Sets the layout resource that is inflated as the {@link View} to be shown
343     * for this Preference. In most cases, the default layout is sufficient for
344     * custom Preference objects and only the widget layout needs to be changed.
345     * <p>
346     * This layout should contain a {@link ViewGroup} with ID
347     * {@link android.R.id#widget_frame} to be the parent of the specific widget
348     * for this Preference. It should similarly contain
349     * {@link android.R.id#title} and {@link android.R.id#summary}.
350     *
351     * @param layoutResId The layout resource ID to be inflated and returned as
352     *            a {@link View}.
353     * @see #setWidgetLayoutResource(int)
354     */
355    public void setLayoutResource(int layoutResId) {
356        if (layoutResId != mLayoutResId) {
357            // Layout changed
358            mHasSpecifiedLayout = true;
359        }
360
361        mLayoutResId = layoutResId;
362    }
363
364    /**
365     * Gets the layout resource that will be shown as the {@link View} for this Preference.
366     *
367     * @return The layout resource ID.
368     */
369    public int getLayoutResource() {
370        return mLayoutResId;
371    }
372
373    /**
374     * Sets The layout for the controllable widget portion of this Preference. This
375     * is inflated into the main layout. For example, a {@link CheckBoxPreference}
376     * would specify a custom layout (consisting of just the CheckBox) here,
377     * instead of creating its own main layout.
378     *
379     * @param widgetLayoutResId The layout resource ID to be inflated into the
380     *            main layout.
381     * @see #setLayoutResource(int)
382     */
383    public void setWidgetLayoutResource(int widgetLayoutResId) {
384        if (widgetLayoutResId != mWidgetLayoutResId) {
385            // Layout changed
386            mHasSpecifiedLayout = true;
387        }
388        mWidgetLayoutResId = widgetLayoutResId;
389    }
390
391    /**
392     * Gets the layout resource for the controllable widget portion of this Preference.
393     *
394     * @return The layout resource ID.
395     */
396    public int getWidgetLayoutResource() {
397        return mWidgetLayoutResId;
398    }
399
400    /**
401     * Gets the View that will be shown in the {@link PreferenceActivity}.
402     *
403     * @param convertView The old View to reuse, if possible. Note: You should
404     *            check that this View is non-null and of an appropriate type
405     *            before using. If it is not possible to convert this View to
406     *            display the correct data, this method can create a new View.
407     * @param parent The parent that this View will eventually be attached to.
408     * @return Returns the same Preference object, for chaining multiple calls
409     *         into a single statement.
410     * @see #onCreateView(ViewGroup)
411     * @see #onBindView(View)
412     */
413    public View getView(View convertView, ViewGroup parent) {
414        if (convertView == null) {
415            convertView = onCreateView(parent);
416        }
417        onBindView(convertView);
418        return convertView;
419    }
420
421    /**
422     * Creates the View to be shown for this Preference in the
423     * {@link PreferenceActivity}. The default behavior is to inflate the main
424     * layout of this Preference (see {@link #setLayoutResource(int)}. If
425     * changing this behavior, please specify a {@link ViewGroup} with ID
426     * {@link android.R.id#widget_frame}.
427     * <p>
428     * Make sure to call through to the superclass's implementation.
429     *
430     * @param parent The parent that this View will eventually be attached to.
431     * @return The View that displays this Preference.
432     * @see #onBindView(View)
433     */
434    protected View onCreateView(ViewGroup parent) {
435        final LayoutInflater layoutInflater =
436            (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
437
438        final View layout = layoutInflater.inflate(mLayoutResId, parent, false);
439
440        if (mWidgetLayoutResId != 0) {
441            final ViewGroup widgetFrame = (ViewGroup)layout.findViewById(com.android.internal.R.id.widget_frame);
442            layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);
443        }
444
445        return layout;
446    }
447
448    /**
449     * Binds the created View to the data for this Preference.
450     * <p>
451     * This is a good place to grab references to custom Views in the layout and
452     * set properties on them.
453     * <p>
454     * Make sure to call through to the superclass's implementation.
455     *
456     * @param view The View that shows this Preference.
457     * @see #onCreateView(ViewGroup)
458     */
459    protected void onBindView(View view) {
460        TextView textView = (TextView) view.findViewById(com.android.internal.R.id.title);
461        if (textView != null) {
462            textView.setText(getTitle());
463        }
464
465        textView = (TextView) view.findViewById(com.android.internal.R.id.summary);
466        if (textView != null) {
467            final CharSequence summary = getSummary();
468            if (!TextUtils.isEmpty(summary)) {
469                if (textView.getVisibility() != View.VISIBLE) {
470                    textView.setVisibility(View.VISIBLE);
471                }
472
473                textView.setText(getSummary());
474            } else {
475                if (textView.getVisibility() != View.GONE) {
476                    textView.setVisibility(View.GONE);
477                }
478            }
479        }
480
481        if (mShouldDisableView) {
482            setEnabledStateOnViews(view, isEnabled());
483        }
484    }
485
486    /**
487     * Makes sure the view (and any children) get the enabled state changed.
488     */
489    private void setEnabledStateOnViews(View v, boolean enabled) {
490        v.setEnabled(enabled);
491
492        if (v instanceof ViewGroup) {
493            final ViewGroup vg = (ViewGroup) v;
494            for (int i = vg.getChildCount() - 1; i >= 0; i--) {
495                setEnabledStateOnViews(vg.getChildAt(i), enabled);
496            }
497        }
498    }
499
500    /**
501     * Sets the order of this Preference with respect to other
502     * Preference objects on the same level. If this is not specified, the
503     * default behavior is to sort alphabetically. The
504     * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order
505     * Preference objects based on the order they appear in the XML.
506     *
507     * @param order The order for this Preference. A lower value will be shown
508     *            first. Use {@link #DEFAULT_ORDER} to sort alphabetically or
509     *            allow ordering from XML.
510     * @see PreferenceGroup#setOrderingAsAdded(boolean)
511     * @see #DEFAULT_ORDER
512     */
513    public void setOrder(int order) {
514        if (order != mOrder) {
515            mOrder = order;
516
517            // Reorder the list
518            notifyHierarchyChanged();
519        }
520    }
521
522    /**
523     * Gets the order of this Preference with respect to other Preference objects
524     * on the same level.
525     *
526     * @return The order of this Preference.
527     * @see #setOrder(int)
528     */
529    public int getOrder() {
530        return mOrder;
531    }
532
533    /**
534     * Sets the title for this Preference with a CharSequence.
535     * This title will be placed into the ID
536     * {@link android.R.id#title} within the View created by
537     * {@link #onCreateView(ViewGroup)}.
538     *
539     * @param title The title for this Preference.
540     */
541    public void setTitle(CharSequence title) {
542        if (title == null && mTitle != null || title != null && !title.equals(mTitle)) {
543            mTitle = title;
544            notifyChanged();
545        }
546    }
547
548    /**
549     * Sets the title for this Preference with a resource ID.
550     *
551     * @see #setTitle(CharSequence)
552     * @param titleResId The title as a resource ID.
553     */
554    public void setTitle(int titleResId) {
555        setTitle(mContext.getString(titleResId));
556    }
557
558    /**
559     * Returns the title of this Preference.
560     *
561     * @return The title.
562     * @see #setTitle(CharSequence)
563     */
564    public CharSequence getTitle() {
565        return mTitle;
566    }
567
568    /**
569     * Returns the summary of this Preference.
570     *
571     * @return The summary.
572     * @see #setSummary(CharSequence)
573     */
574    public CharSequence getSummary() {
575        return mSummary;
576    }
577
578    /**
579     * Sets the summary for this Preference with a CharSequence.
580     *
581     * @param summary The summary for the preference.
582     */
583    public void setSummary(CharSequence summary) {
584        if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) {
585            mSummary = summary;
586            notifyChanged();
587        }
588    }
589
590    /**
591     * Sets the summary for this Preference with a resource ID.
592     *
593     * @see #setSummary(CharSequence)
594     * @param summaryResId The summary as a resource.
595     */
596    public void setSummary(int summaryResId) {
597        setSummary(mContext.getString(summaryResId));
598    }
599
600    /**
601     * Sets whether this Preference is enabled. If disabled, it will
602     * not handle clicks.
603     *
604     * @param enabled Set true to enable it.
605     */
606    public void setEnabled(boolean enabled) {
607        if (mEnabled != enabled) {
608            mEnabled = enabled;
609
610            // Enabled state can change dependent preferences' states, so notify
611            notifyDependencyChange(shouldDisableDependents());
612
613            notifyChanged();
614        }
615    }
616
617    /**
618     * Checks whether this Preference should be enabled in the list.
619     *
620     * @return True if this Preference is enabled, false otherwise.
621     */
622    public boolean isEnabled() {
623        return mEnabled && mDependencyMet;
624    }
625
626    /**
627     * Sets whether this Preference is selectable.
628     *
629     * @param selectable Set true to make it selectable.
630     */
631    public void setSelectable(boolean selectable) {
632        if (mSelectable != selectable) {
633            mSelectable = selectable;
634            notifyChanged();
635        }
636    }
637
638    /**
639     * Checks whether this Preference should be selectable in the list.
640     *
641     * @return True if it is selectable, false otherwise.
642     */
643    public boolean isSelectable() {
644        return mSelectable;
645    }
646
647    /**
648     * Sets whether this Preference should disable its view when it gets
649     * disabled.
650     * <p>
651     * For example, set this and {@link #setEnabled(boolean)} to false for
652     * preferences that are only displaying information and 1) should not be
653     * clickable 2) should not have the view set to the disabled state.
654     *
655     * @param shouldDisableView Set true if this preference should disable its view
656     *            when the preference is disabled.
657     */
658    public void setShouldDisableView(boolean shouldDisableView) {
659        mShouldDisableView = shouldDisableView;
660        notifyChanged();
661    }
662
663    /**
664     * Checks whether this Preference should disable its view when it's action is disabled.
665     * @see #setShouldDisableView(boolean)
666     * @return True if it should disable the view.
667     */
668    public boolean getShouldDisableView() {
669        return mShouldDisableView;
670    }
671
672    /**
673     * Returns a unique ID for this Preference.  This ID should be unique across all
674     * Preference objects in a hierarchy.
675     *
676     * @return A unique ID for this Preference.
677     */
678    long getId() {
679        return mId;
680    }
681
682    /**
683     * Processes a click on the preference. This includes saving the value to
684     * the {@link SharedPreferences}. However, the overridden method should
685     * call {@link #callChangeListener(Object)} to make sure the client wants to
686     * update the preference's state with the new value.
687     */
688    protected void onClick() {
689    }
690
691    /**
692     * Sets the key for this Preference, which is used as a key to the
693     * {@link SharedPreferences}. This should be unique for the package.
694     *
695     * @param key The key for the preference.
696     */
697    public void setKey(String key) {
698        mKey = key;
699
700        if (mRequiresKey && !hasKey()) {
701            requireKey();
702        }
703    }
704
705    /**
706     * Gets the key for this Preference, which is also the key used for storing
707     * values into SharedPreferences.
708     *
709     * @return The key.
710     */
711    public String getKey() {
712        return mKey;
713    }
714
715    /**
716     * Checks whether the key is present, and if it isn't throws an
717     * exception. This should be called by subclasses that store preferences in
718     * the {@link SharedPreferences}.
719     *
720     * @throws IllegalStateException If there is no key assigned.
721     */
722    void requireKey() {
723        if (mKey == null) {
724            throw new IllegalStateException("Preference does not have a key assigned.");
725        }
726
727        mRequiresKey = true;
728    }
729
730    /**
731     * Checks whether this Preference has a valid key.
732     *
733     * @return True if the key exists and is not a blank string, false otherwise.
734     */
735    public boolean hasKey() {
736        return !TextUtils.isEmpty(mKey);
737    }
738
739    /**
740     * Checks whether this Preference is persistent. If it is, it stores its value(s) into
741     * the persistent {@link SharedPreferences} storage.
742     *
743     * @return True if it is persistent.
744     */
745    public boolean isPersistent() {
746        return mPersistent;
747    }
748
749    /**
750     * Checks whether, at the given time this method is called,
751     * this Preference should store/restore its value(s) into the
752     * {@link SharedPreferences}. This, at minimum, checks whether this
753     * Preference is persistent and it currently has a key. Before you
754     * save/restore from the {@link SharedPreferences}, check this first.
755     *
756     * @return True if it should persist the value.
757     */
758    protected boolean shouldPersist() {
759        return mPreferenceManager != null && isPersistent() && hasKey();
760    }
761
762    /**
763     * Sets whether this Preference is persistent. When persistent,
764     * it stores its value(s) into the persistent {@link SharedPreferences}
765     * storage.
766     *
767     * @param persistent Set true if it should store its value(s) into the {@link SharedPreferences}.
768     */
769    public void setPersistent(boolean persistent) {
770        mPersistent = persistent;
771    }
772
773    /**
774     * Call this method after the user changes the preference, but before the
775     * internal state is set. This allows the client to ignore the user value.
776     *
777     * @param newValue The new value of this Preference.
778     * @return True if the user value should be set as the preference
779     *         value (and persisted).
780     */
781    protected boolean callChangeListener(Object newValue) {
782        return mOnChangeListener == null ? true : mOnChangeListener.onPreferenceChange(this, newValue);
783    }
784
785    /**
786     * Sets the callback to be invoked when this Preference is changed by the
787     * user (but before the internal state has been updated).
788     *
789     * @param onPreferenceChangeListener The callback to be invoked.
790     */
791    public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) {
792        mOnChangeListener = onPreferenceChangeListener;
793    }
794
795    /**
796     * Returns the callback to be invoked when this Preference is changed by the
797     * user (but before the internal state has been updated).
798     *
799     * @return The callback to be invoked.
800     */
801    public OnPreferenceChangeListener getOnPreferenceChangeListener() {
802        return mOnChangeListener;
803    }
804
805    /**
806     * Sets the callback to be invoked when this Preference is clicked.
807     *
808     * @param onPreferenceClickListener The callback to be invoked.
809     */
810    public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
811        mOnClickListener = onPreferenceClickListener;
812    }
813
814    /**
815     * Returns the callback to be invoked when this Preference is clicked.
816     *
817     * @return The callback to be invoked.
818     */
819    public OnPreferenceClickListener getOnPreferenceClickListener() {
820        return mOnClickListener;
821    }
822
823    /**
824     * Called when a click should be performed.
825     *
826     * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
827     *            listener should be called in the proper order (between other
828     *            processing). May be null.
829     */
830    void performClick(PreferenceScreen preferenceScreen) {
831
832        if (!isEnabled()) {
833            return;
834        }
835
836        onClick();
837
838        if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
839            return;
840        }
841
842        PreferenceManager preferenceManager = getPreferenceManager();
843        if (preferenceManager != null) {
844            PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
845                    .getOnPreferenceTreeClickListener();
846            if (preferenceScreen != null && listener != null
847                    && listener.onPreferenceTreeClick(preferenceScreen, this)) {
848                return;
849            }
850        }
851
852        if (mIntent != null) {
853            Context context = getContext();
854            context.startActivity(mIntent);
855        }
856    }
857
858    /**
859     * Returns the {@link android.content.Context} of this Preference.
860     * Each Preference in a Preference hierarchy can be
861     * from different Context (for example, if multiple activities provide preferences into a single
862     * {@link PreferenceActivity}). This Context will be used to save the Preference values.
863     *
864     * @return The Context of this Preference.
865     */
866    public Context getContext() {
867        return mContext;
868    }
869
870    /**
871     * Returns the {@link SharedPreferences} where this Preference can read its
872     * value(s). Usually, it's easier to use one of the helper read methods:
873     * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)},
874     * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)},
875     * {@link #getPersistedString(String)}. To save values, see
876     * {@link #getEditor()}.
877     * <p>
878     * In some cases, writes to the {@link #getEditor()} will not be committed
879     * right away and hence not show up in the returned
880     * {@link SharedPreferences}, this is intended behavior to improve
881     * performance.
882     *
883     * @return The {@link SharedPreferences} where this Preference reads its
884     *         value(s), or null if it isn't attached to a Preference hierarchy.
885     * @see #getEditor()
886     */
887    public SharedPreferences getSharedPreferences() {
888        if (mPreferenceManager == null) {
889            return null;
890        }
891
892        return mPreferenceManager.getSharedPreferences();
893    }
894
895    /**
896     * Returns an {@link SharedPreferences.Editor} where this Preference can
897     * save its value(s). Usually it's easier to use one of the helper save
898     * methods: {@link #persistBoolean(boolean)}, {@link #persistFloat(float)},
899     * {@link #persistInt(int)}, {@link #persistLong(long)},
900     * {@link #persistString(String)}. To read values, see
901     * {@link #getSharedPreferences()}. If {@link #shouldCommit()} returns
902     * true, it is this Preference's responsibility to commit.
903     * <p>
904     * In some cases, writes to this will not be committed right away and hence
905     * not show up in the SharedPreferences, this is intended behavior to
906     * improve performance.
907     *
908     * @return A {@link SharedPreferences.Editor} where this preference saves
909     *         its value(s), or null if it isn't attached to a Preference
910     *         hierarchy.
911     * @see #shouldCommit()
912     * @see #getSharedPreferences()
913     */
914    public SharedPreferences.Editor getEditor() {
915        if (mPreferenceManager == null) {
916            return null;
917        }
918
919        return mPreferenceManager.getEditor();
920    }
921
922    /**
923     * Returns whether the {@link Preference} should commit its saved value(s) in
924     * {@link #getEditor()}. This may return false in situations where batch
925     * committing is being done (by the manager) to improve performance.
926     *
927     * @return Whether the Preference should commit its saved value(s).
928     * @see #getEditor()
929     */
930    public boolean shouldCommit() {
931        if (mPreferenceManager == null) {
932            return false;
933        }
934
935        return mPreferenceManager.shouldCommit();
936    }
937
938    /**
939     * Compares Preference objects based on order (if set), otherwise alphabetically on the titles.
940     *
941     * @param another The Preference to compare to this one.
942     * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
943     *          greater than 0 if this Preference sorts after <var>another</var>.
944     */
945    public int compareTo(Preference another) {
946        if (mOrder != DEFAULT_ORDER
947                || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) {
948            // Do order comparison
949            return mOrder - another.mOrder;
950        } else if (mTitle == null) {
951            return 1;
952        } else if (another.mTitle == null) {
953            return -1;
954        } else {
955            // Do name comparison
956            return CharSequences.compareToIgnoreCase(mTitle, another.mTitle);
957        }
958    }
959
960    /**
961     * Sets the internal change listener.
962     *
963     * @param listener The listener.
964     * @see #notifyChanged()
965     */
966    final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) {
967        mListener = listener;
968    }
969
970    /**
971     * Should be called when the data of this {@link Preference} has changed.
972     */
973    protected void notifyChanged() {
974        if (mListener != null) {
975            mListener.onPreferenceChange(this);
976        }
977    }
978
979    /**
980     * Should be called when a Preference has been
981     * added/removed from this group, or the ordering should be
982     * re-evaluated.
983     */
984    protected void notifyHierarchyChanged() {
985        if (mListener != null) {
986            mListener.onPreferenceHierarchyChange(this);
987        }
988    }
989
990    /**
991     * Gets the {@link PreferenceManager} that manages this Preference object's tree.
992     *
993     * @return The {@link PreferenceManager}.
994     */
995    public PreferenceManager getPreferenceManager() {
996        return mPreferenceManager;
997    }
998
999    /**
1000     * Called when this Preference has been attached to a Preference hierarchy.
1001     * Make sure to call the super implementation.
1002     *
1003     * @param preferenceManager The PreferenceManager of the hierarchy.
1004     */
1005    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
1006        mPreferenceManager = preferenceManager;
1007
1008        mId = preferenceManager.getNextId();
1009
1010        dispatchSetInitialValue();
1011    }
1012
1013    /**
1014     * Called when the Preference hierarchy has been attached to the
1015     * {@link PreferenceActivity}. This can also be called when this
1016     * Preference has been attached to a group that was already attached
1017     * to the {@link PreferenceActivity}.
1018     */
1019    protected void onAttachedToActivity() {
1020        // At this point, the hierarchy that this preference is in is connected
1021        // with all other preferences.
1022        registerDependency();
1023    }
1024
1025    private void registerDependency() {
1026
1027        if (TextUtils.isEmpty(mDependencyKey)) return;
1028
1029        Preference preference = findPreferenceInHierarchy(mDependencyKey);
1030        if (preference != null) {
1031            preference.registerDependent(this);
1032        } else {
1033            throw new IllegalStateException("Dependency \"" + mDependencyKey
1034                    + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\"");
1035        }
1036    }
1037
1038    private void unregisterDependency() {
1039        if (mDependencyKey != null) {
1040            final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey);
1041            if (oldDependency != null) {
1042                oldDependency.unregisterDependent(this);
1043            }
1044        }
1045    }
1046
1047    /**
1048     * Finds a Preference in this hierarchy (the whole thing,
1049     * even above/below your {@link PreferenceScreen} screen break) with the given
1050     * key.
1051     * <p>
1052     * This only functions after we have been attached to a hierarchy.
1053     *
1054     * @param key The key of the Preference to find.
1055     * @return The Preference that uses the given key.
1056     */
1057    protected Preference findPreferenceInHierarchy(String key) {
1058        if (TextUtils.isEmpty(key) || mPreferenceManager == null) {
1059            return null;
1060        }
1061
1062        return mPreferenceManager.findPreference(key);
1063    }
1064
1065    /**
1066     * Adds a dependent Preference on this Preference so we can notify it.
1067     * Usually, the dependent Preference registers itself (it's good for it to
1068     * know it depends on something), so please use
1069     * {@link Preference#setDependency(String)} on the dependent Preference.
1070     *
1071     * @param dependent The dependent Preference that will be enabled/disabled
1072     *            according to the state of this Preference.
1073     */
1074    private void registerDependent(Preference dependent) {
1075        if (mDependents == null) {
1076            mDependents = new ArrayList<Preference>();
1077        }
1078
1079        mDependents.add(dependent);
1080
1081        dependent.onDependencyChanged(this, shouldDisableDependents());
1082    }
1083
1084    /**
1085     * Removes a dependent Preference on this Preference.
1086     *
1087     * @param dependent The dependent Preference that will be enabled/disabled
1088     *            according to the state of this Preference.
1089     * @return Returns the same Preference object, for chaining multiple calls
1090     *         into a single statement.
1091     */
1092    private void unregisterDependent(Preference dependent) {
1093        if (mDependents != null) {
1094            mDependents.remove(dependent);
1095        }
1096    }
1097
1098    /**
1099     * Notifies any listening dependents of a change that affects the
1100     * dependency.
1101     *
1102     * @param disableDependents Whether this Preference should disable
1103     *            its dependents.
1104     */
1105    public void notifyDependencyChange(boolean disableDependents) {
1106        final List<Preference> dependents = mDependents;
1107
1108        if (dependents == null) {
1109            return;
1110        }
1111
1112        final int dependentsCount = dependents.size();
1113        for (int i = 0; i < dependentsCount; i++) {
1114            dependents.get(i).onDependencyChanged(this, disableDependents);
1115        }
1116    }
1117
1118    /**
1119     * Called when the dependency changes.
1120     *
1121     * @param dependency The Preference that this Preference depends on.
1122     * @param disableDependent Set true to disable this Preference.
1123     */
1124    public void onDependencyChanged(Preference dependency, boolean disableDependent) {
1125        if (mDependencyMet == disableDependent) {
1126            mDependencyMet = !disableDependent;
1127
1128            // Enabled state can change dependent preferences' states, so notify
1129            notifyDependencyChange(shouldDisableDependents());
1130
1131            notifyChanged();
1132        }
1133    }
1134
1135    /**
1136     * Checks whether this preference's dependents should currently be
1137     * disabled.
1138     *
1139     * @return True if the dependents should be disabled, otherwise false.
1140     */
1141    public boolean shouldDisableDependents() {
1142        return !isEnabled();
1143    }
1144
1145    /**
1146     * Sets the key of a Preference that this Preference will depend on. If that
1147     * Preference is not set or is off, this Preference will be disabled.
1148     *
1149     * @param dependencyKey The key of the Preference that this depends on.
1150     */
1151    public void setDependency(String dependencyKey) {
1152        // Unregister the old dependency, if we had one
1153        unregisterDependency();
1154
1155        // Register the new
1156        mDependencyKey = dependencyKey;
1157        registerDependency();
1158    }
1159
1160    /**
1161     * Returns the key of the dependency on this Preference.
1162     *
1163     * @return The key of the dependency.
1164     * @see #setDependency(String)
1165     */
1166    public String getDependency() {
1167        return mDependencyKey;
1168    }
1169
1170    /**
1171     * Called when this Preference is being removed from the hierarchy. You
1172     * should remove any references to this Preference that you know about. Make
1173     * sure to call through to the superclass implementation.
1174     */
1175    protected void onPrepareForRemoval() {
1176        unregisterDependency();
1177    }
1178
1179    /**
1180     * Sets the default value for this Preference, which will be set either if
1181     * persistence is off or persistence is on and the preference is not found
1182     * in the persistent storage.
1183     *
1184     * @param defaultValue The default value.
1185     */
1186    public void setDefaultValue(Object defaultValue) {
1187        mDefaultValue = defaultValue;
1188    }
1189
1190    private void dispatchSetInitialValue() {
1191        // By now, we know if we are persistent.
1192        final boolean shouldPersist = shouldPersist();
1193        if (!shouldPersist || !getSharedPreferences().contains(mKey)) {
1194            if (mDefaultValue != null) {
1195                onSetInitialValue(false, mDefaultValue);
1196            }
1197        } else {
1198            onSetInitialValue(true, null);
1199        }
1200    }
1201
1202    /**
1203     * Implement this to set the initial value of the Preference.
1204     * <p>
1205     * If <var>restorePersistedValue</var> is true, you should restore the
1206     * Preference value from the {@link android.content.SharedPreferences}. If
1207     * <var>restorePersistedValue</var> is false, you should set the Preference
1208     * value to defaultValue that is given (and possibly store to SharedPreferences
1209     * if {@link #shouldPersist()} is true).
1210     * <p>
1211     * This may not always be called. One example is if it should not persist
1212     * but there is no default value given.
1213     *
1214     * @param restorePersistedValue True to restore the persisted value;
1215     *            false to use the given <var>defaultValue</var>.
1216     * @param defaultValue The default value for this Preference. Only use this
1217     *            if <var>restorePersistedValue</var> is false.
1218     */
1219    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
1220    }
1221
1222    private void tryCommit(SharedPreferences.Editor editor) {
1223        if (mPreferenceManager.shouldCommit()) {
1224            editor.commit();
1225        }
1226    }
1227
1228    /**
1229     * Attempts to persist a String to the {@link android.content.SharedPreferences}.
1230     * <p>
1231     * This will check if this Preference is persistent, get an editor from
1232     * the {@link PreferenceManager}, put in the string, and check if we should commit (and
1233     * commit if so).
1234     *
1235     * @param value The value to persist.
1236     * @return True if the Preference is persistent. (This is not whether the
1237     *         value was persisted, since we may not necessarily commit if there
1238     *         will be a batch commit later.)
1239     * @see #getPersistedString(String)
1240     */
1241    protected boolean persistString(String value) {
1242        if (shouldPersist()) {
1243            // Shouldn't store null
1244            if (value == getPersistedString(null)) {
1245                // It's already there, so the same as persisting
1246                return true;
1247            }
1248
1249            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1250            editor.putString(mKey, value);
1251            tryCommit(editor);
1252            return true;
1253        }
1254        return false;
1255    }
1256
1257    /**
1258     * Attempts to get a persisted String from the {@link android.content.SharedPreferences}.
1259     * <p>
1260     * This will check if this Preference is persistent, get the SharedPreferences
1261     * from the {@link PreferenceManager}, and get the value.
1262     *
1263     * @param defaultReturnValue The default value to return if either the
1264     *            Preference is not persistent or the Preference is not in the
1265     *            shared preferences.
1266     * @return The value from the SharedPreferences or the default return
1267     *         value.
1268     * @see #persistString(String)
1269     */
1270    protected String getPersistedString(String defaultReturnValue) {
1271        if (!shouldPersist()) {
1272            return defaultReturnValue;
1273        }
1274
1275        return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
1276    }
1277
1278    /**
1279     * Attempts to persist a set of Strings to the {@link android.content.SharedPreferences}.
1280     * <p>
1281     * This will check if this Preference is persistent, get an editor from
1282     * the {@link PreferenceManager}, put in the strings, and check if we should commit (and
1283     * commit if so).
1284     *
1285     * @param values The values to persist.
1286     * @return True if the Preference is persistent. (This is not whether the
1287     *         value was persisted, since we may not necessarily commit if there
1288     *         will be a batch commit later.)
1289     * @see #getPersistedString(Set)
1290     *
1291     * @hide Pending API approval
1292     */
1293    protected boolean persistStringSet(Set<String> values) {
1294        if (shouldPersist()) {
1295            // Shouldn't store null
1296            if (values.equals(getPersistedStringSet(null))) {
1297                // It's already there, so the same as persisting
1298                return true;
1299            }
1300
1301            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1302            editor.putStringSet(mKey, values);
1303            tryCommit(editor);
1304            return true;
1305        }
1306        return false;
1307    }
1308
1309    /**
1310     * Attempts to get a persisted set of Strings from the
1311     * {@link android.content.SharedPreferences}.
1312     * <p>
1313     * This will check if this Preference is persistent, get the SharedPreferences
1314     * from the {@link PreferenceManager}, and get the value.
1315     *
1316     * @param defaultReturnValue The default value to return if either the
1317     *            Preference is not persistent or the Preference is not in the
1318     *            shared preferences.
1319     * @return The value from the SharedPreferences or the default return
1320     *         value.
1321     * @see #persistStringSet(Set)
1322     *
1323     * @hide Pending API approval
1324     */
1325    protected Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
1326        if (!shouldPersist()) {
1327            return defaultReturnValue;
1328        }
1329
1330        return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue);
1331    }
1332
1333    /**
1334     * Attempts to persist an int to the {@link android.content.SharedPreferences}.
1335     *
1336     * @param value The value to persist.
1337     * @return True if the Preference is persistent. (This is not whether the
1338     *         value was persisted, since we may not necessarily commit if there
1339     *         will be a batch commit later.)
1340     * @see #persistString(String)
1341     * @see #getPersistedInt(int)
1342     */
1343    protected boolean persistInt(int value) {
1344        if (shouldPersist()) {
1345            if (value == getPersistedInt(~value)) {
1346                // It's already there, so the same as persisting
1347                return true;
1348            }
1349
1350            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1351            editor.putInt(mKey, value);
1352            tryCommit(editor);
1353            return true;
1354        }
1355        return false;
1356    }
1357
1358    /**
1359     * Attempts to get a persisted int from the {@link android.content.SharedPreferences}.
1360     *
1361     * @param defaultReturnValue The default value to return if either this
1362     *            Preference is not persistent or this Preference is not in the
1363     *            SharedPreferences.
1364     * @return The value from the SharedPreferences or the default return
1365     *         value.
1366     * @see #getPersistedString(String)
1367     * @see #persistInt(int)
1368     */
1369    protected int getPersistedInt(int defaultReturnValue) {
1370        if (!shouldPersist()) {
1371            return defaultReturnValue;
1372        }
1373
1374        return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
1375    }
1376
1377    /**
1378     * Attempts to persist a float to the {@link android.content.SharedPreferences}.
1379     *
1380     * @param value The value to persist.
1381     * @return True if this Preference is persistent. (This is not whether the
1382     *         value was persisted, since we may not necessarily commit if there
1383     *         will be a batch commit later.)
1384     * @see #persistString(String)
1385     * @see #getPersistedFloat(float)
1386     */
1387    protected boolean persistFloat(float value) {
1388        if (shouldPersist()) {
1389            if (value == getPersistedFloat(Float.NaN)) {
1390                // It's already there, so the same as persisting
1391                return true;
1392            }
1393
1394            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1395            editor.putFloat(mKey, value);
1396            tryCommit(editor);
1397            return true;
1398        }
1399        return false;
1400    }
1401
1402    /**
1403     * Attempts to get a persisted float from the {@link android.content.SharedPreferences}.
1404     *
1405     * @param defaultReturnValue The default value to return if either this
1406     *            Preference is not persistent or this Preference is not in the
1407     *            SharedPreferences.
1408     * @return The value from the SharedPreferences or the default return
1409     *         value.
1410     * @see #getPersistedString(String)
1411     * @see #persistFloat(float)
1412     */
1413    protected float getPersistedFloat(float defaultReturnValue) {
1414        if (!shouldPersist()) {
1415            return defaultReturnValue;
1416        }
1417
1418        return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
1419    }
1420
1421    /**
1422     * Attempts to persist a long to the {@link android.content.SharedPreferences}.
1423     *
1424     * @param value The value to persist.
1425     * @return True if this Preference is persistent. (This is not whether the
1426     *         value was persisted, since we may not necessarily commit if there
1427     *         will be a batch commit later.)
1428     * @see #persistString(String)
1429     * @see #getPersistedLong(long)
1430     */
1431    protected boolean persistLong(long value) {
1432        if (shouldPersist()) {
1433            if (value == getPersistedLong(~value)) {
1434                // It's already there, so the same as persisting
1435                return true;
1436            }
1437
1438            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1439            editor.putLong(mKey, value);
1440            tryCommit(editor);
1441            return true;
1442        }
1443        return false;
1444    }
1445
1446    /**
1447     * Attempts to get a persisted long from the {@link android.content.SharedPreferences}.
1448     *
1449     * @param defaultReturnValue The default value to return if either this
1450     *            Preference is not persistent or this Preference is not in the
1451     *            SharedPreferences.
1452     * @return The value from the SharedPreferences or the default return
1453     *         value.
1454     * @see #getPersistedString(String)
1455     * @see #persistLong(long)
1456     */
1457    protected long getPersistedLong(long defaultReturnValue) {
1458        if (!shouldPersist()) {
1459            return defaultReturnValue;
1460        }
1461
1462        return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
1463    }
1464
1465    /**
1466     * Attempts to persist a boolean to the {@link android.content.SharedPreferences}.
1467     *
1468     * @param value The value to persist.
1469     * @return True if this Preference is persistent. (This is not whether the
1470     *         value was persisted, since we may not necessarily commit if there
1471     *         will be a batch commit later.)
1472     * @see #persistString(String)
1473     * @see #getPersistedBoolean(boolean)
1474     */
1475    protected boolean persistBoolean(boolean value) {
1476        if (shouldPersist()) {
1477            if (value == getPersistedBoolean(!value)) {
1478                // It's already there, so the same as persisting
1479                return true;
1480            }
1481
1482            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1483            editor.putBoolean(mKey, value);
1484            tryCommit(editor);
1485            return true;
1486        }
1487        return false;
1488    }
1489
1490    /**
1491     * Attempts to get a persisted boolean from the {@link android.content.SharedPreferences}.
1492     *
1493     * @param defaultReturnValue The default value to return if either this
1494     *            Preference is not persistent or this Preference is not in the
1495     *            SharedPreferences.
1496     * @return The value from the SharedPreferences or the default return
1497     *         value.
1498     * @see #getPersistedString(String)
1499     * @see #persistBoolean(boolean)
1500     */
1501    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
1502        if (!shouldPersist()) {
1503            return defaultReturnValue;
1504        }
1505
1506        return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
1507    }
1508
1509    boolean hasSpecifiedLayout() {
1510        return mHasSpecifiedLayout;
1511    }
1512
1513    @Override
1514    public String toString() {
1515        return getFilterableStringBuilder().toString();
1516    }
1517
1518    /**
1519     * Returns the text that will be used to filter this Preference depending on
1520     * user input.
1521     * <p>
1522     * If overridding and calling through to the superclass, make sure to prepend
1523     * your additions with a space.
1524     *
1525     * @return Text as a {@link StringBuilder} that will be used to filter this
1526     *         preference. By default, this is the title and summary
1527     *         (concatenated with a space).
1528     */
1529    StringBuilder getFilterableStringBuilder() {
1530        StringBuilder sb = new StringBuilder();
1531        CharSequence title = getTitle();
1532        if (!TextUtils.isEmpty(title)) {
1533            sb.append(title).append(' ');
1534        }
1535        CharSequence summary = getSummary();
1536        if (!TextUtils.isEmpty(summary)) {
1537            sb.append(summary).append(' ');
1538        }
1539        if (sb.length() > 0) {
1540            // Drop the last space
1541            sb.setLength(sb.length() - 1);
1542        }
1543        return sb;
1544    }
1545
1546    /**
1547     * Store this Preference hierarchy's frozen state into the given container.
1548     *
1549     * @param container The Bundle in which to save the instance of this Preference.
1550     *
1551     * @see #restoreHierarchyState
1552     * @see #onSaveInstanceState
1553     */
1554    public void saveHierarchyState(Bundle container) {
1555        dispatchSaveInstanceState(container);
1556    }
1557
1558    /**
1559     * Called by {@link #saveHierarchyState} to store the instance for this Preference and its children.
1560     * May be overridden to modify how the save happens for children. For example, some
1561     * Preference objects may want to not store an instance for their children.
1562     *
1563     * @param container The Bundle in which to save the instance of this Preference.
1564     *
1565     * @see #saveHierarchyState
1566     * @see #onSaveInstanceState
1567     */
1568    void dispatchSaveInstanceState(Bundle container) {
1569        if (hasKey()) {
1570            mBaseMethodCalled = false;
1571            Parcelable state = onSaveInstanceState();
1572            if (!mBaseMethodCalled) {
1573                throw new IllegalStateException(
1574                        "Derived class did not call super.onSaveInstanceState()");
1575            }
1576            if (state != null) {
1577                container.putParcelable(mKey, state);
1578            }
1579        }
1580    }
1581
1582    /**
1583     * Hook allowing a Preference to generate a representation of its internal
1584     * state that can later be used to create a new instance with that same
1585     * state. This state should only contain information that is not persistent
1586     * or can be reconstructed later.
1587     *
1588     * @return A Parcelable object containing the current dynamic state of
1589     *         this Preference, or null if there is nothing interesting to save.
1590     *         The default implementation returns null.
1591     * @see #onRestoreInstanceState
1592     * @see #saveHierarchyState
1593     */
1594    protected Parcelable onSaveInstanceState() {
1595        mBaseMethodCalled = true;
1596        return BaseSavedState.EMPTY_STATE;
1597    }
1598
1599    /**
1600     * Restore this Preference hierarchy's previously saved state from the given container.
1601     *
1602     * @param container The Bundle that holds the previously saved state.
1603     *
1604     * @see #saveHierarchyState
1605     * @see #onRestoreInstanceState
1606     */
1607    public void restoreHierarchyState(Bundle container) {
1608        dispatchRestoreInstanceState(container);
1609    }
1610
1611    /**
1612     * Called by {@link #restoreHierarchyState} to retrieve the saved state for this
1613     * Preference and its children. May be overridden to modify how restoring
1614     * happens to the children of a Preference. For example, some Preference objects may
1615     * not want to save state for their children.
1616     *
1617     * @param container The Bundle that holds the previously saved state.
1618     * @see #restoreHierarchyState
1619     * @see #onRestoreInstanceState
1620     */
1621    void dispatchRestoreInstanceState(Bundle container) {
1622        if (hasKey()) {
1623            Parcelable state = container.getParcelable(mKey);
1624            if (state != null) {
1625                mBaseMethodCalled = false;
1626                onRestoreInstanceState(state);
1627                if (!mBaseMethodCalled) {
1628                    throw new IllegalStateException(
1629                            "Derived class did not call super.onRestoreInstanceState()");
1630                }
1631            }
1632        }
1633    }
1634
1635    /**
1636     * Hook allowing a Preference to re-apply a representation of its internal
1637     * state that had previously been generated by {@link #onSaveInstanceState}.
1638     * This function will never be called with a null state.
1639     *
1640     * @param state The saved state that had previously been returned by
1641     *            {@link #onSaveInstanceState}.
1642     * @see #onSaveInstanceState
1643     * @see #restoreHierarchyState
1644     */
1645    protected void onRestoreInstanceState(Parcelable state) {
1646        mBaseMethodCalled = true;
1647        if (state != BaseSavedState.EMPTY_STATE && state != null) {
1648            throw new IllegalArgumentException("Wrong state class -- expecting Preference State");
1649        }
1650    }
1651
1652    /**
1653     * A base class for managing the instance state of a {@link Preference}.
1654     */
1655    public static class BaseSavedState extends AbsSavedState {
1656        public BaseSavedState(Parcel source) {
1657            super(source);
1658        }
1659
1660        public BaseSavedState(Parcelable superState) {
1661            super(superState);
1662        }
1663
1664        public static final Parcelable.Creator<BaseSavedState> CREATOR =
1665                new Parcelable.Creator<BaseSavedState>() {
1666            public BaseSavedState createFromParcel(Parcel in) {
1667                return new BaseSavedState(in);
1668            }
1669
1670            public BaseSavedState[] newArray(int size) {
1671                return new BaseSavedState[size];
1672            }
1673        };
1674    }
1675
1676}
1677