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