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     * @hide
943     */
944    public void performClick(PreferenceScreen preferenceScreen) {
945
946        if (!isEnabled()) {
947            return;
948        }
949
950        onClick();
951
952        if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
953            return;
954        }
955
956        PreferenceManager preferenceManager = getPreferenceManager();
957        if (preferenceManager != null) {
958            PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
959                    .getOnPreferenceTreeClickListener();
960            if (preferenceScreen != null && listener != null
961                    && listener.onPreferenceTreeClick(preferenceScreen, this)) {
962                return;
963            }
964        }
965
966        if (mIntent != null) {
967            Context context = getContext();
968            context.startActivity(mIntent);
969        }
970    }
971
972    /**
973     * Allows a Preference to intercept key events without having focus.
974     * For example, SeekBarPreference uses this to intercept +/- to adjust
975     * the progress.
976     * @return True if the Preference handled the key. Returns false by default.
977     * @hide
978     */
979    public boolean onKey(View v, int keyCode, KeyEvent event) {
980        return false;
981    }
982
983    /**
984     * Returns the {@link android.content.Context} of this Preference.
985     * Each Preference in a Preference hierarchy can be
986     * from different Context (for example, if multiple activities provide preferences into a single
987     * {@link PreferenceActivity}). This Context will be used to save the Preference values.
988     *
989     * @return The Context of this Preference.
990     */
991    public Context getContext() {
992        return mContext;
993    }
994
995    /**
996     * Returns the {@link SharedPreferences} where this Preference can read its
997     * value(s). Usually, it's easier to use one of the helper read methods:
998     * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)},
999     * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)},
1000     * {@link #getPersistedString(String)}. To save values, see
1001     * {@link #getEditor()}.
1002     * <p>
1003     * In some cases, writes to the {@link #getEditor()} will not be committed
1004     * right away and hence not show up in the returned
1005     * {@link SharedPreferences}, this is intended behavior to improve
1006     * performance.
1007     *
1008     * @return The {@link SharedPreferences} where this Preference reads its
1009     *         value(s), or null if it isn't attached to a Preference hierarchy.
1010     * @see #getEditor()
1011     */
1012    public SharedPreferences getSharedPreferences() {
1013        if (mPreferenceManager == null) {
1014            return null;
1015        }
1016
1017        return mPreferenceManager.getSharedPreferences();
1018    }
1019
1020    /**
1021     * Returns an {@link SharedPreferences.Editor} where this Preference can
1022     * save its value(s). Usually it's easier to use one of the helper save
1023     * methods: {@link #persistBoolean(boolean)}, {@link #persistFloat(float)},
1024     * {@link #persistInt(int)}, {@link #persistLong(long)},
1025     * {@link #persistString(String)}. To read values, see
1026     * {@link #getSharedPreferences()}. If {@link #shouldCommit()} returns
1027     * true, it is this Preference's responsibility to commit.
1028     * <p>
1029     * In some cases, writes to this will not be committed right away and hence
1030     * not show up in the SharedPreferences, this is intended behavior to
1031     * improve performance.
1032     *
1033     * @return A {@link SharedPreferences.Editor} where this preference saves
1034     *         its value(s), or null if it isn't attached to a Preference
1035     *         hierarchy.
1036     * @see #shouldCommit()
1037     * @see #getSharedPreferences()
1038     */
1039    public SharedPreferences.Editor getEditor() {
1040        if (mPreferenceManager == null) {
1041            return null;
1042        }
1043
1044        return mPreferenceManager.getEditor();
1045    }
1046
1047    /**
1048     * Returns whether the {@link Preference} should commit its saved value(s) in
1049     * {@link #getEditor()}. This may return false in situations where batch
1050     * committing is being done (by the manager) to improve performance.
1051     *
1052     * @return Whether the Preference should commit its saved value(s).
1053     * @see #getEditor()
1054     */
1055    public boolean shouldCommit() {
1056        if (mPreferenceManager == null) {
1057            return false;
1058        }
1059
1060        return mPreferenceManager.shouldCommit();
1061    }
1062
1063    /**
1064     * Compares Preference objects based on order (if set), otherwise alphabetically on the titles.
1065     *
1066     * @param another The Preference to compare to this one.
1067     * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
1068     *          greater than 0 if this Preference sorts after <var>another</var>.
1069     */
1070    public int compareTo(Preference another) {
1071        if (mOrder != DEFAULT_ORDER
1072                || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) {
1073            // Do order comparison
1074            return mOrder - another.mOrder;
1075        } else if (mTitle == null) {
1076            return 1;
1077        } else if (another.mTitle == null) {
1078            return -1;
1079        } else {
1080            // Do name comparison
1081            return CharSequences.compareToIgnoreCase(mTitle, another.mTitle);
1082        }
1083    }
1084
1085    /**
1086     * Sets the internal change listener.
1087     *
1088     * @param listener The listener.
1089     * @see #notifyChanged()
1090     */
1091    final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) {
1092        mListener = listener;
1093    }
1094
1095    /**
1096     * Should be called when the data of this {@link Preference} has changed.
1097     */
1098    protected void notifyChanged() {
1099        if (mListener != null) {
1100            mListener.onPreferenceChange(this);
1101        }
1102    }
1103
1104    /**
1105     * Should be called when a Preference has been
1106     * added/removed from this group, or the ordering should be
1107     * re-evaluated.
1108     */
1109    protected void notifyHierarchyChanged() {
1110        if (mListener != null) {
1111            mListener.onPreferenceHierarchyChange(this);
1112        }
1113    }
1114
1115    /**
1116     * Gets the {@link PreferenceManager} that manages this Preference object's tree.
1117     *
1118     * @return The {@link PreferenceManager}.
1119     */
1120    public PreferenceManager getPreferenceManager() {
1121        return mPreferenceManager;
1122    }
1123
1124    /**
1125     * Called when this Preference has been attached to a Preference hierarchy.
1126     * Make sure to call the super implementation.
1127     *
1128     * @param preferenceManager The PreferenceManager of the hierarchy.
1129     */
1130    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
1131        mPreferenceManager = preferenceManager;
1132
1133        mId = preferenceManager.getNextId();
1134
1135        dispatchSetInitialValue();
1136    }
1137
1138    /**
1139     * Called when the Preference hierarchy has been attached to the
1140     * {@link PreferenceActivity}. This can also be called when this
1141     * Preference has been attached to a group that was already attached
1142     * to the {@link PreferenceActivity}.
1143     */
1144    protected void onAttachedToActivity() {
1145        // At this point, the hierarchy that this preference is in is connected
1146        // with all other preferences.
1147        registerDependency();
1148    }
1149
1150    private void registerDependency() {
1151
1152        if (TextUtils.isEmpty(mDependencyKey)) return;
1153
1154        Preference preference = findPreferenceInHierarchy(mDependencyKey);
1155        if (preference != null) {
1156            preference.registerDependent(this);
1157        } else {
1158            throw new IllegalStateException("Dependency \"" + mDependencyKey
1159                    + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\"");
1160        }
1161    }
1162
1163    private void unregisterDependency() {
1164        if (mDependencyKey != null) {
1165            final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey);
1166            if (oldDependency != null) {
1167                oldDependency.unregisterDependent(this);
1168            }
1169        }
1170    }
1171
1172    /**
1173     * Finds a Preference in this hierarchy (the whole thing,
1174     * even above/below your {@link PreferenceScreen} screen break) with the given
1175     * key.
1176     * <p>
1177     * This only functions after we have been attached to a hierarchy.
1178     *
1179     * @param key The key of the Preference to find.
1180     * @return The Preference that uses the given key.
1181     */
1182    protected Preference findPreferenceInHierarchy(String key) {
1183        if (TextUtils.isEmpty(key) || mPreferenceManager == null) {
1184            return null;
1185        }
1186
1187        return mPreferenceManager.findPreference(key);
1188    }
1189
1190    /**
1191     * Adds a dependent Preference on this Preference so we can notify it.
1192     * Usually, the dependent Preference registers itself (it's good for it to
1193     * know it depends on something), so please use
1194     * {@link Preference#setDependency(String)} on the dependent Preference.
1195     *
1196     * @param dependent The dependent Preference that will be enabled/disabled
1197     *            according to the state of this Preference.
1198     */
1199    private void registerDependent(Preference dependent) {
1200        if (mDependents == null) {
1201            mDependents = new ArrayList<Preference>();
1202        }
1203
1204        mDependents.add(dependent);
1205
1206        dependent.onDependencyChanged(this, shouldDisableDependents());
1207    }
1208
1209    /**
1210     * Removes a dependent Preference on this Preference.
1211     *
1212     * @param dependent The dependent Preference that will be enabled/disabled
1213     *            according to the state of this Preference.
1214     * @return Returns the same Preference object, for chaining multiple calls
1215     *         into a single statement.
1216     */
1217    private void unregisterDependent(Preference dependent) {
1218        if (mDependents != null) {
1219            mDependents.remove(dependent);
1220        }
1221    }
1222
1223    /**
1224     * Notifies any listening dependents of a change that affects the
1225     * dependency.
1226     *
1227     * @param disableDependents Whether this Preference should disable
1228     *            its dependents.
1229     */
1230    public void notifyDependencyChange(boolean disableDependents) {
1231        final List<Preference> dependents = mDependents;
1232
1233        if (dependents == null) {
1234            return;
1235        }
1236
1237        final int dependentsCount = dependents.size();
1238        for (int i = 0; i < dependentsCount; i++) {
1239            dependents.get(i).onDependencyChanged(this, disableDependents);
1240        }
1241    }
1242
1243    /**
1244     * Called when the dependency changes.
1245     *
1246     * @param dependency The Preference that this Preference depends on.
1247     * @param disableDependent Set true to disable this Preference.
1248     */
1249    public void onDependencyChanged(Preference dependency, boolean disableDependent) {
1250        if (mDependencyMet == disableDependent) {
1251            mDependencyMet = !disableDependent;
1252
1253            // Enabled state can change dependent preferences' states, so notify
1254            notifyDependencyChange(shouldDisableDependents());
1255
1256            notifyChanged();
1257        }
1258    }
1259
1260    /**
1261     * Checks whether this preference's dependents should currently be
1262     * disabled.
1263     *
1264     * @return True if the dependents should be disabled, otherwise false.
1265     */
1266    public boolean shouldDisableDependents() {
1267        return !isEnabled();
1268    }
1269
1270    /**
1271     * Sets the key of a Preference that this Preference will depend on. If that
1272     * Preference is not set or is off, this Preference will be disabled.
1273     *
1274     * @param dependencyKey The key of the Preference that this depends on.
1275     */
1276    public void setDependency(String dependencyKey) {
1277        // Unregister the old dependency, if we had one
1278        unregisterDependency();
1279
1280        // Register the new
1281        mDependencyKey = dependencyKey;
1282        registerDependency();
1283    }
1284
1285    /**
1286     * Returns the key of the dependency on this Preference.
1287     *
1288     * @return The key of the dependency.
1289     * @see #setDependency(String)
1290     */
1291    public String getDependency() {
1292        return mDependencyKey;
1293    }
1294
1295    /**
1296     * Called when this Preference is being removed from the hierarchy. You
1297     * should remove any references to this Preference that you know about. Make
1298     * sure to call through to the superclass implementation.
1299     */
1300    protected void onPrepareForRemoval() {
1301        unregisterDependency();
1302    }
1303
1304    /**
1305     * Sets the default value for this Preference, which will be set either if
1306     * persistence is off or persistence is on and the preference is not found
1307     * in the persistent storage.
1308     *
1309     * @param defaultValue The default value.
1310     */
1311    public void setDefaultValue(Object defaultValue) {
1312        mDefaultValue = defaultValue;
1313    }
1314
1315    private void dispatchSetInitialValue() {
1316        // By now, we know if we are persistent.
1317        final boolean shouldPersist = shouldPersist();
1318        if (!shouldPersist || !getSharedPreferences().contains(mKey)) {
1319            if (mDefaultValue != null) {
1320                onSetInitialValue(false, mDefaultValue);
1321            }
1322        } else {
1323            onSetInitialValue(true, null);
1324        }
1325    }
1326
1327    /**
1328     * Implement this to set the initial value of the Preference.
1329     * <p>
1330     * If <var>restorePersistedValue</var> is true, you should restore the
1331     * Preference value from the {@link android.content.SharedPreferences}. If
1332     * <var>restorePersistedValue</var> is false, you should set the Preference
1333     * value to defaultValue that is given (and possibly store to SharedPreferences
1334     * if {@link #shouldPersist()} is true).
1335     * <p>
1336     * This may not always be called. One example is if it should not persist
1337     * but there is no default value given.
1338     *
1339     * @param restorePersistedValue True to restore the persisted value;
1340     *            false to use the given <var>defaultValue</var>.
1341     * @param defaultValue The default value for this Preference. Only use this
1342     *            if <var>restorePersistedValue</var> is false.
1343     */
1344    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
1345    }
1346
1347    private void tryCommit(SharedPreferences.Editor editor) {
1348        if (mPreferenceManager.shouldCommit()) {
1349            try {
1350                editor.apply();
1351            } catch (AbstractMethodError unused) {
1352                // The app injected its own pre-Gingerbread
1353                // SharedPreferences.Editor implementation without
1354                // an apply method.
1355                editor.commit();
1356            }
1357        }
1358    }
1359
1360    /**
1361     * Attempts to persist a String to the {@link android.content.SharedPreferences}.
1362     * <p>
1363     * This will check if this Preference is persistent, get an editor from
1364     * the {@link PreferenceManager}, put in the string, and check if we should commit (and
1365     * commit if so).
1366     *
1367     * @param value The value to persist.
1368     * @return True if the Preference is persistent. (This is not whether the
1369     *         value was persisted, since we may not necessarily commit if there
1370     *         will be a batch commit later.)
1371     * @see #getPersistedString(String)
1372     */
1373    protected boolean persistString(String value) {
1374        if (shouldPersist()) {
1375            // Shouldn't store null
1376            if (value == getPersistedString(null)) {
1377                // It's already there, so the same as persisting
1378                return true;
1379            }
1380
1381            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1382            editor.putString(mKey, value);
1383            tryCommit(editor);
1384            return true;
1385        }
1386        return false;
1387    }
1388
1389    /**
1390     * Attempts to get a persisted String from the {@link android.content.SharedPreferences}.
1391     * <p>
1392     * This will check if this Preference is persistent, get the SharedPreferences
1393     * from the {@link PreferenceManager}, and get the value.
1394     *
1395     * @param defaultReturnValue The default value to return if either the
1396     *            Preference is not persistent or the Preference is not in the
1397     *            shared preferences.
1398     * @return The value from the SharedPreferences or the default return
1399     *         value.
1400     * @see #persistString(String)
1401     */
1402    protected String getPersistedString(String defaultReturnValue) {
1403        if (!shouldPersist()) {
1404            return defaultReturnValue;
1405        }
1406
1407        return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
1408    }
1409
1410    /**
1411     * Attempts to persist a set of Strings to the {@link android.content.SharedPreferences}.
1412     * <p>
1413     * This will check if this Preference is persistent, get an editor from
1414     * the {@link PreferenceManager}, put in the strings, and check if we should commit (and
1415     * commit if so).
1416     *
1417     * @param values The values to persist.
1418     * @return True if the Preference is persistent. (This is not whether the
1419     *         value was persisted, since we may not necessarily commit if there
1420     *         will be a batch commit later.)
1421     * @see #getPersistedString(Set)
1422     *
1423     * @hide Pending API approval
1424     */
1425    protected boolean persistStringSet(Set<String> values) {
1426        if (shouldPersist()) {
1427            // Shouldn't store null
1428            if (values.equals(getPersistedStringSet(null))) {
1429                // It's already there, so the same as persisting
1430                return true;
1431            }
1432
1433            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1434            editor.putStringSet(mKey, values);
1435            tryCommit(editor);
1436            return true;
1437        }
1438        return false;
1439    }
1440
1441    /**
1442     * Attempts to get a persisted set of Strings from the
1443     * {@link android.content.SharedPreferences}.
1444     * <p>
1445     * This will check if this Preference is persistent, get the SharedPreferences
1446     * from the {@link PreferenceManager}, and get the value.
1447     *
1448     * @param defaultReturnValue The default value to return if either the
1449     *            Preference is not persistent or the Preference is not in the
1450     *            shared preferences.
1451     * @return The value from the SharedPreferences or the default return
1452     *         value.
1453     * @see #persistStringSet(Set)
1454     *
1455     * @hide Pending API approval
1456     */
1457    protected Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
1458        if (!shouldPersist()) {
1459            return defaultReturnValue;
1460        }
1461
1462        return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue);
1463    }
1464
1465    /**
1466     * Attempts to persist an int to the {@link android.content.SharedPreferences}.
1467     *
1468     * @param value The value to persist.
1469     * @return True if the Preference is persistent. (This is not whether the
1470     *         value was persisted, since we may not necessarily commit if there
1471     *         will be a batch commit later.)
1472     * @see #persistString(String)
1473     * @see #getPersistedInt(int)
1474     */
1475    protected boolean persistInt(int value) {
1476        if (shouldPersist()) {
1477            if (value == getPersistedInt(~value)) {
1478                // It's already there, so the same as persisting
1479                return true;
1480            }
1481
1482            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1483            editor.putInt(mKey, value);
1484            tryCommit(editor);
1485            return true;
1486        }
1487        return false;
1488    }
1489
1490    /**
1491     * Attempts to get a persisted int from the {@link android.content.SharedPreferences}.
1492     *
1493     * @param defaultReturnValue The default value to return if either this
1494     *            Preference is not persistent or this Preference is not in the
1495     *            SharedPreferences.
1496     * @return The value from the SharedPreferences or the default return
1497     *         value.
1498     * @see #getPersistedString(String)
1499     * @see #persistInt(int)
1500     */
1501    protected int getPersistedInt(int defaultReturnValue) {
1502        if (!shouldPersist()) {
1503            return defaultReturnValue;
1504        }
1505
1506        return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
1507    }
1508
1509    /**
1510     * Attempts to persist a float to the {@link android.content.SharedPreferences}.
1511     *
1512     * @param value The value to persist.
1513     * @return True if this Preference is persistent. (This is not whether the
1514     *         value was persisted, since we may not necessarily commit if there
1515     *         will be a batch commit later.)
1516     * @see #persistString(String)
1517     * @see #getPersistedFloat(float)
1518     */
1519    protected boolean persistFloat(float value) {
1520        if (shouldPersist()) {
1521            if (value == getPersistedFloat(Float.NaN)) {
1522                // It's already there, so the same as persisting
1523                return true;
1524            }
1525
1526            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1527            editor.putFloat(mKey, value);
1528            tryCommit(editor);
1529            return true;
1530        }
1531        return false;
1532    }
1533
1534    /**
1535     * Attempts to get a persisted float from the {@link android.content.SharedPreferences}.
1536     *
1537     * @param defaultReturnValue The default value to return if either this
1538     *            Preference is not persistent or this Preference is not in the
1539     *            SharedPreferences.
1540     * @return The value from the SharedPreferences or the default return
1541     *         value.
1542     * @see #getPersistedString(String)
1543     * @see #persistFloat(float)
1544     */
1545    protected float getPersistedFloat(float defaultReturnValue) {
1546        if (!shouldPersist()) {
1547            return defaultReturnValue;
1548        }
1549
1550        return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
1551    }
1552
1553    /**
1554     * Attempts to persist a long to the {@link android.content.SharedPreferences}.
1555     *
1556     * @param value The value to persist.
1557     * @return True if this Preference is persistent. (This is not whether the
1558     *         value was persisted, since we may not necessarily commit if there
1559     *         will be a batch commit later.)
1560     * @see #persistString(String)
1561     * @see #getPersistedLong(long)
1562     */
1563    protected boolean persistLong(long value) {
1564        if (shouldPersist()) {
1565            if (value == getPersistedLong(~value)) {
1566                // It's already there, so the same as persisting
1567                return true;
1568            }
1569
1570            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1571            editor.putLong(mKey, value);
1572            tryCommit(editor);
1573            return true;
1574        }
1575        return false;
1576    }
1577
1578    /**
1579     * Attempts to get a persisted long from the {@link android.content.SharedPreferences}.
1580     *
1581     * @param defaultReturnValue The default value to return if either this
1582     *            Preference is not persistent or this Preference is not in the
1583     *            SharedPreferences.
1584     * @return The value from the SharedPreferences or the default return
1585     *         value.
1586     * @see #getPersistedString(String)
1587     * @see #persistLong(long)
1588     */
1589    protected long getPersistedLong(long defaultReturnValue) {
1590        if (!shouldPersist()) {
1591            return defaultReturnValue;
1592        }
1593
1594        return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
1595    }
1596
1597    /**
1598     * Attempts to persist a boolean to the {@link android.content.SharedPreferences}.
1599     *
1600     * @param value The value to persist.
1601     * @return True if this Preference is persistent. (This is not whether the
1602     *         value was persisted, since we may not necessarily commit if there
1603     *         will be a batch commit later.)
1604     * @see #persistString(String)
1605     * @see #getPersistedBoolean(boolean)
1606     */
1607    protected boolean persistBoolean(boolean value) {
1608        if (shouldPersist()) {
1609            if (value == getPersistedBoolean(!value)) {
1610                // It's already there, so the same as persisting
1611                return true;
1612            }
1613
1614            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1615            editor.putBoolean(mKey, value);
1616            tryCommit(editor);
1617            return true;
1618        }
1619        return false;
1620    }
1621
1622    /**
1623     * Attempts to get a persisted boolean from the {@link android.content.SharedPreferences}.
1624     *
1625     * @param defaultReturnValue The default value to return if either this
1626     *            Preference is not persistent or this Preference is not in the
1627     *            SharedPreferences.
1628     * @return The value from the SharedPreferences or the default return
1629     *         value.
1630     * @see #getPersistedString(String)
1631     * @see #persistBoolean(boolean)
1632     */
1633    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
1634        if (!shouldPersist()) {
1635            return defaultReturnValue;
1636        }
1637
1638        return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
1639    }
1640
1641    boolean hasSpecifiedLayout() {
1642        return mHasSpecifiedLayout;
1643    }
1644
1645    @Override
1646    public String toString() {
1647        return getFilterableStringBuilder().toString();
1648    }
1649
1650    /**
1651     * Returns the text that will be used to filter this Preference depending on
1652     * user input.
1653     * <p>
1654     * If overridding and calling through to the superclass, make sure to prepend
1655     * your additions with a space.
1656     *
1657     * @return Text as a {@link StringBuilder} that will be used to filter this
1658     *         preference. By default, this is the title and summary
1659     *         (concatenated with a space).
1660     */
1661    StringBuilder getFilterableStringBuilder() {
1662        StringBuilder sb = new StringBuilder();
1663        CharSequence title = getTitle();
1664        if (!TextUtils.isEmpty(title)) {
1665            sb.append(title).append(' ');
1666        }
1667        CharSequence summary = getSummary();
1668        if (!TextUtils.isEmpty(summary)) {
1669            sb.append(summary).append(' ');
1670        }
1671        if (sb.length() > 0) {
1672            // Drop the last space
1673            sb.setLength(sb.length() - 1);
1674        }
1675        return sb;
1676    }
1677
1678    /**
1679     * Store this Preference hierarchy's frozen state into the given container.
1680     *
1681     * @param container The Bundle in which to save the instance of this Preference.
1682     *
1683     * @see #restoreHierarchyState
1684     * @see #onSaveInstanceState
1685     */
1686    public void saveHierarchyState(Bundle container) {
1687        dispatchSaveInstanceState(container);
1688    }
1689
1690    /**
1691     * Called by {@link #saveHierarchyState} to store the instance for this Preference and its children.
1692     * May be overridden to modify how the save happens for children. For example, some
1693     * Preference objects may want to not store an instance for their children.
1694     *
1695     * @param container The Bundle in which to save the instance of this Preference.
1696     *
1697     * @see #saveHierarchyState
1698     * @see #onSaveInstanceState
1699     */
1700    void dispatchSaveInstanceState(Bundle container) {
1701        if (hasKey()) {
1702            mBaseMethodCalled = false;
1703            Parcelable state = onSaveInstanceState();
1704            if (!mBaseMethodCalled) {
1705                throw new IllegalStateException(
1706                        "Derived class did not call super.onSaveInstanceState()");
1707            }
1708            if (state != null) {
1709                container.putParcelable(mKey, state);
1710            }
1711        }
1712    }
1713
1714    /**
1715     * Hook allowing a Preference to generate a representation of its internal
1716     * state that can later be used to create a new instance with that same
1717     * state. This state should only contain information that is not persistent
1718     * or can be reconstructed later.
1719     *
1720     * @return A Parcelable object containing the current dynamic state of
1721     *         this Preference, or null if there is nothing interesting to save.
1722     *         The default implementation returns null.
1723     * @see #onRestoreInstanceState
1724     * @see #saveHierarchyState
1725     */
1726    protected Parcelable onSaveInstanceState() {
1727        mBaseMethodCalled = true;
1728        return BaseSavedState.EMPTY_STATE;
1729    }
1730
1731    /**
1732     * Restore this Preference hierarchy's previously saved state from the given container.
1733     *
1734     * @param container The Bundle that holds the previously saved state.
1735     *
1736     * @see #saveHierarchyState
1737     * @see #onRestoreInstanceState
1738     */
1739    public void restoreHierarchyState(Bundle container) {
1740        dispatchRestoreInstanceState(container);
1741    }
1742
1743    /**
1744     * Called by {@link #restoreHierarchyState} to retrieve the saved state for this
1745     * Preference and its children. May be overridden to modify how restoring
1746     * happens to the children of a Preference. For example, some Preference objects may
1747     * not want to save state for their children.
1748     *
1749     * @param container The Bundle that holds the previously saved state.
1750     * @see #restoreHierarchyState
1751     * @see #onRestoreInstanceState
1752     */
1753    void dispatchRestoreInstanceState(Bundle container) {
1754        if (hasKey()) {
1755            Parcelable state = container.getParcelable(mKey);
1756            if (state != null) {
1757                mBaseMethodCalled = false;
1758                onRestoreInstanceState(state);
1759                if (!mBaseMethodCalled) {
1760                    throw new IllegalStateException(
1761                            "Derived class did not call super.onRestoreInstanceState()");
1762                }
1763            }
1764        }
1765    }
1766
1767    /**
1768     * Hook allowing a Preference to re-apply a representation of its internal
1769     * state that had previously been generated by {@link #onSaveInstanceState}.
1770     * This function will never be called with a null state.
1771     *
1772     * @param state The saved state that had previously been returned by
1773     *            {@link #onSaveInstanceState}.
1774     * @see #onSaveInstanceState
1775     * @see #restoreHierarchyState
1776     */
1777    protected void onRestoreInstanceState(Parcelable state) {
1778        mBaseMethodCalled = true;
1779        if (state != BaseSavedState.EMPTY_STATE && state != null) {
1780            throw new IllegalArgumentException("Wrong state class -- expecting Preference State");
1781        }
1782    }
1783
1784    /**
1785     * A base class for managing the instance state of a {@link Preference}.
1786     */
1787    public static class BaseSavedState extends AbsSavedState {
1788        public BaseSavedState(Parcel source) {
1789            super(source);
1790        }
1791
1792        public BaseSavedState(Parcelable superState) {
1793            super(superState);
1794        }
1795
1796        public static final Parcelable.Creator<BaseSavedState> CREATOR =
1797                new Parcelable.Creator<BaseSavedState>() {
1798            public BaseSavedState createFromParcel(Parcel in) {
1799                return new BaseSavedState(in);
1800            }
1801
1802            public BaseSavedState[] newArray(int size) {
1803                return new BaseSavedState[size];
1804            }
1805        };
1806    }
1807
1808}
1809