OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / preference / Preference.java
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
17 package android.preference;
18
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.SharedPreferences;
25 import android.content.res.TypedArray;
26 import android.os.Bundle;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.text.TextUtils;
30 import android.util.AttributeSet;
31 import com.android.internal.util.CharSequences;
32 import android.view.AbsSavedState;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.widget.ListView;
37 import 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  */
66 public 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 }