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