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